This is actually exactly what Minix does. It sets up the TSS to point to the current task's TCB so that the registers get saved in there automatically and don't need to be copied off the kernel stack.Brendan wrote:Unless my wits have completely failed me, this tutorial assumes a CPL=3 stack per task, a CPL=0 stack per task and a CPL=0 stack that's shared by all tasks. This is (IMHO) quite unusual, but explains a lot about previous discussion
tss on stack???
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
-
- Member
- Posts: 1600
- Joined: Wed Oct 18, 2006 11:59 am
- Location: Vienna/Austria
- Contact:
Re:tss on stack???
@brendan: I know it as "If a butterfly takes a crap somewhere in russia, this might cause a tidal wave on the other side of the world". Something I've read in some terry pratchett book. *rofl*
as for the removing-a-task-stuff: In my OS, tasks are currently removed immediately. First general clean up, then the clean up in the kernel - by a dedicated kernel thread. So no worry, we aren't in the stack of the thread to be deleted. *gg*
as for the removing-a-task-stuff: In my OS, tasks are currently removed immediately. First general clean up, then the clean up in the kernel - by a dedicated kernel thread. So no worry, we aren't in the stack of the thread to be deleted. *gg*
... the osdever formerly known as beyond infinity ...
BlueillusionOS iso image
BlueillusionOS iso image
Re:tss on stack???
Hi,
Specifically, the SS0 and ESP0 fields in the TSS must be updated every task switch to point to the new taskl's kernel stack, and the CPU itself handles all stack switching (except for within the task switch code).
If you want to use C for your kernel, I'd also recommend the elegant method of parameter passing posted by Colonel Kernel. Also, I'd still do the thread switch code itself in a seperate routine.
Combining all of this gives something like:
[continued]
That's done with the examples I've been posting, sort of.GLneo wrote:i think your right, i have been looking at very diferent tutorials and tried to put them togheter , i would like to use both a CPL=3 stack and a CPL=0 stack per task, how is THAT done?,
Specifically, the SS0 and ESP0 fields in the TSS must be updated every task switch to point to the new taskl's kernel stack, and the CPU itself handles all stack switching (except for within the task switch code).
If you want to use C for your kernel, I'd also recommend the elegant method of parameter passing posted by Colonel Kernel. Also, I'd still do the thread switch code itself in a seperate routine.
Combining all of this gives something like:
Code: Select all
;IRQ 0 assembly stub. Must be called with interrupts disabled (i.e. use
; an "interrupt gate" in the IDT and not a "trap gate").
IRQhandler00:
<push DS, ES, FS, GS if necessary>
pusha
<reload DS and ES (and maybe FS, GS) with values the kernel expects, if necessary>
push esp ;Push the only parameter to the C function (the address of CPU state on the stack)
call _Timer_IRQ_handler ;Call the C function (which must send the EOI)
add esp,4 ;Remove the C function's parameters from the stack
cmp eax,esp ;Did the C function request a task switch?
je .skipSwitch ; no
call do_task_switch ; yes, call the assembly code that handles task switches
.skipSwitch:
popa
<pop DS, ES, FS, GS if necessary>
iretd
Code: Select all
;Kernel API assembly stub. Must be called with interrupts enabled (which is
; what you'd expect when CPL=3 code was running anyway).
kernelAPIentry:
<push DS, ES, FS, GS if necessary>
pusha
<reload DS and ES (and maybe FS, GS) with values the kernel expects, if necessary>
push esp ;Push the only parameter to the C function (the address of CPU state on the stack)
call _kernel_API_entry_point ;Call the C function
add esp,4 ;Remove the C function's parameters from the stack
cmp eax,esp ;Did the C function request a task switch?
je .skipSwitch ; no, skip the task switch
cli ;Disable interrupts for the task switch code
call do_task_switch ;Call the assembly code that handles task switches
sti ;Restore interrupts
.skipSwitch:
popa
<pop DS, ES, FS, GS if necessary>
iretd
Code: Select all
;Do a task switch. This code must be called with interrupts disabled.
;All general registers trashed (shouldn't matter as they're always saved
;before calling this code, and restored after this code runs.
;
;Input
; eax Must contain the top of stack for the task being switched to
do_task_switch:
mov ebx,[currentTaskStructure]
mov [ebx+TASK_STRUCT.currentESP],esp
<work out how much CPU time the last task used and update "[ebx + TASK_STRUCT.timeUsed]">
<convert EAX into address of task structure and put it in ESI>
mov ebx,[esi+TASK_STRUCT.kernelStackTop]
mov [address_of_system_TSS + TSS.ESP0], ebx
mov ebx,[esi+TASK_STRUCT.pageDirectory]
mov ecx,cr3
cmp ecx,ebx ;Does address space need to be changed?
je .skipCR3 ; no, skip it
mov cr3,ebx ; yes, update CR3
<do something about FPU/MMX/SSE/SSE2 state saving here>
mov esp,eax ;Switch stacks
ret
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:tss on stack???
[continued]
Then you'd have the C code:
Some notes:
I haven't tested any of this, but you should be able to cut & paste it, then fix all the (lots and lots) of errors caused by missing stuff and it should (hopefully) work. I strongly recommend that you do not do this - instead read it, understand it then implement your own version.
Inside the "do_task_switch" routine there's a line that says "<convert EAX into address of task structure and put it in ESI>". This part depends on how you're keeping track of tasks. I like to have a "Task Control Block" (or structure) that contains things like the task's name, how many messages are in it's queue, which options it's using, etc. If this structure is 8 KB and includes the task's kernel stack, and if the structure is also aligned on an 8 KB boundary, then you could replace the "<convert EAX into address of task structure and put it in ESI>" with something like:
How you do this depends on how you keep track of the rest of the task's data...
All of the code in the "STACK_DATA *_kernel_API_entry_point(STACK_DATA *callerRegs)" function is an example only. I'm not sure how well the compiler will optimize the "switch/case/case" stuff and I'd probably recommend using a call table instead (to avoid a pile of conditional branches).
You must create the "STACK_DATA *" structure/data type so that it matches the data pushed on the stack at the start of "IRQhandler00", "kernelAPIentry" and any other kernel entry points.
I'm not sure if you'll be treating the data segment registers (DS, ES, FS and GS) as constants or allowing them to be changed - you should be able to make allowances either way.
Cheers,
Brendan
Then you'd have the C code:
Code: Select all
unsigned int SystemTimerTick = 0;
STACK_DATA *_Timer_IRQ_handler(STACK_DATA *callerRegs) {
SystemTimerTick++;
outportb(0x20, 0x20); //Send the EOI
if( did_this_task's_time_run_out() == YES) {
return find_a_new_task_to_run(callerRegs);
} else {
return callerRegs;
}
}
Code: Select all
STACK_DATA *_kernel_API_entry_point(STACK_DATA *callerRegs) {
switch(callerRegs->EAX) {
case API_FUNCTION_A:
return function_A(callerRegs); // Might cause task switch
case API_FUNCTION_B:
return function_B(callerRegs); // Might cause task switch
case API_FUNCTION_C:
function_C(callerRegs); // Never causes task switch
return callerRegs;
default:
callerRegs->EAX = ERROR_UNDEFINED_FUNCTION;
return callerRegs;
}
}
Code: Select all
STACK_DATA *function_A(STACK_DATA *callerRegs) {
<this is an example kernel API function>
return callerRegs;
}
STACK_DATA *function_B(STACK_DATA *callerRegs) {
<this is an example kernel API function>
return callerRegs;
}
void function_C(STACK_DATA *callerRegs) {
<this is an example kernel API function>
}
I haven't tested any of this, but you should be able to cut & paste it, then fix all the (lots and lots) of errors caused by missing stuff and it should (hopefully) work. I strongly recommend that you do not do this - instead read it, understand it then implement your own version.
Inside the "do_task_switch" routine there's a line that says "<convert EAX into address of task structure and put it in ESI>". This part depends on how you're keeping track of tasks. I like to have a "Task Control Block" (or structure) that contains things like the task's name, how many messages are in it's queue, which options it's using, etc. If this structure is 8 KB and includes the task's kernel stack, and if the structure is also aligned on an 8 KB boundary, then you could replace the "<convert EAX into address of task structure and put it in ESI>" with something like:
Code: Select all
mov esi,eax
and esi,0xFFFF8000
All of the code in the "STACK_DATA *_kernel_API_entry_point(STACK_DATA *callerRegs)" function is an example only. I'm not sure how well the compiler will optimize the "switch/case/case" stuff and I'd probably recommend using a call table instead (to avoid a pile of conditional branches).
You must create the "STACK_DATA *" structure/data type so that it matches the data pushed on the stack at the start of "IRQhandler00", "kernelAPIentry" and any other kernel entry points.
I'm not sure if you'll be treating the data segment registers (DS, ES, FS and GS) as constants or allowing them to be changed - you should be able to make allowances either way.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
Looks pretty good to me. A few things, though:
function_C must have a return value. Otherwise you can't really rely on the value of eax in the assembly stub. If a kernel API function wants to switch to another task, it would call the scheduler and return a pointer to the chosen task's STACK_DATA (or TCB, or whatever...).
Also, while it should be OK to allow interrupts between calling the scheduler in C-land and calling do_task_switch in asm, you should not allow pre-emption. The reason is that when the kernel finally returns to user-mode, it should be returning to the task chosen most recently. If you allow the kernel to be preempted after choosing a task but before switching to it, the kernel will eventually switch to that task later, even if this is no longer the right thing to do (e.g. -- if a higher-priority thread has become ready since the pre-emption and subsequent resumption of the kernel). Sorry if this is confusing... I'm working on a better way to explain this when I have time.
Also...
function_C must have a return value. Otherwise you can't really rely on the value of eax in the assembly stub. If a kernel API function wants to switch to another task, it would call the scheduler and return a pointer to the chosen task's STACK_DATA (or TCB, or whatever...).
Also, while it should be OK to allow interrupts between calling the scheduler in C-land and calling do_task_switch in asm, you should not allow pre-emption. The reason is that when the kernel finally returns to user-mode, it should be returning to the task chosen most recently. If you allow the kernel to be preempted after choosing a task but before switching to it, the kernel will eventually switch to that task later, even if this is no longer the right thing to do (e.g. -- if a higher-priority thread has become ready since the pre-emption and subsequent resumption of the kernel). Sorry if this is confusing... I'm working on a better way to explain this when I have time.
Also...
Yeah, definitely use a call table. Most compilers I know of do a poor job of optimizing switch/case.Brendan wrote:All of the code in the "STACK_DATA *_kernel_API_entry_point(STACK_DATA *callerRegs)" function is an example only. I'm not sure how well the compiler will optimize the "switch/case/case" stuff and I'd probably recommend using a call table instead (to avoid a pile of conditional branches).
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
Re:tss on stack???
Hi,
Although if a call table was used it'd be a problem - function C would need to match the other functions.
Of course I'd recommend using a different method of preventing task switches, like a "prevent task switches" flag - disabling interrupts for too long isn't a good idea.
Cheers,
Brendan
The return value for "function C" doesn't matter:Colonel Kernel wrote:function_C must have a return value. Otherwise you can't really rely on the value of eax in the assembly stub. If a kernel API function wants to switch to another task, it would call the scheduler and return a pointer to the chosen task's STACK_DATA (or TCB, or whatever...).
Code: Select all
case API_FUNCTION_C:
function_C(callerRegs); // Never causes task switch
return callerRegs;
For this it'd be possible for any code that causes a task switch to disable interrupts and leave them disabled until the task switch occurs. I didn't want to have interrupts disabled for all kernel API functions...Colonel Kernel wrote:Also, while it should be OK to allow interrupts between calling the scheduler in C-land and calling do_task_switch in asm, you should not allow pre-emption. The reason is that when the kernel finally returns to user-mode, it should be returning to the task chosen most recently. If you allow the kernel to be preempted after choosing a task but before switching to it, the kernel will eventually switch to that task later, even if this is no longer the right thing to do (e.g. -- if a higher-priority thread has become ready since the pre-emption and subsequent resumption of the kernel). Sorry if this is confusing... I'm working on a better way to explain this when I have time.
Of course I'd recommend using a different method of preventing task switches, like a "prevent task switches" flag - disabling interrupts for too long isn't a good idea.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:tss on stack???
doesn't gcc return values on the stack???, how do you get gcc to return in eax, or should i use inline asm to load eax???
Re:tss on stack???
gcc always puts the return value in eax, unless I'm mistaken.GLneo wrote: doesn't gcc return values on the stack???, how do you get gcc to return in eax, or should i use inline asm to load eax???
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
gcc puts return values in eax as long as they're not floats, doubles, structs, or unions. Otherwise, IIRC it copies the return value to some reserved space in the caller's stack frame and returns its address in eax.
<edit>Actually, it might return floats in eax too... I've never tried it.</edit>
<edit>Actually, it might return floats in eax too... I've never tried it.</edit>
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
Re:tss on stack???
ok, i've been working on it but now i'm very confused ??? here is my code:
and then in c:
i dont know if i'm handling return types right or in make_task()what do i need to put in the struct stack_data type ESP???, help, thx ;D
Code: Select all
_task_timer:
pushad
push ds
push es
push fs
push gs
push esp
mov eax, _timer_handler
call eax
add esp,4
cmp eax,esp
je skipSwitch
call do_task_switch
skipSwitch:
pop gs
pop fs
pop es
pop ds
popad
iretd
do_task_switch:
mov esp,eax
ret
Code: Select all
#define QUE_SIZE 128
void task0()
{
while(1)
puts("a");
}
void task1()
{
while(1)
puts("b");
}
unsigned int curtask = 0;
struct stack_data
{
unsigned int gs;
unsigned int fs;
unsigned int es;
unsigned int ds;
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int edi;
unsigned int esi;
unsigned int cs;
unsigned int eflags;
unsigned int eip;
unsigned int esp;
};
struct task_data
{
char name[33];
struct stack_data esp;
unsigned int ss;
unsigned int kstack;
unsigned int ustack;
unsigned int time;
unsigned int priority;
};
int front = 0;
int end = 0;
struct task_data rrq[QUE_SIZE];
stack_data schedule(struct stack_data *regs)
{
cur_task_time_equ(get_pri_time(cur_task_pri()));
front_to_end();
return rrq[front].esp;
}
stack_data timer_handler(struct stack_data *regs)
{
clock();
outport(0x20, 0x20);
if(cur_task_time() > 0)
{
dec_cur_task_time();
return regs;
}
if(cur_task_time() < 1)
return schedule(regs);
}
void make_task(int pri, char *name, void (*entry)())
{
unsigned int *stack;
stack = (unsigned int *)malloc(64);
*stack--;
*stack-- = 0x0202;
*stack-- = 0x08;
*stack-- = (unsigned int)entry;
*stack-- = 0; //ebp
*stack-- = 0; //esp
*stack-- = 0; //edi
*stack-- = 0; //esi
*stack-- = 0; //edx
*stack-- = 0; //ecx
*stack-- = 0; //ebx
*stack-- = 0; //eax
*stack-- = 0x8; //ds
*stack-- = 0x8; //es
*stack-- = 0x8; //fs
*stack = 0x8; //gs
strncpy(rrq[end].name, name, 32);
rrq[end].esp = (struct stack_data)stack;
rrq[end].ss = 0x8;
rrq[end].priority = pri;
rrq[end].time = get_pri_time(pri);
end++;
}
void start_sys()
{
make_task(3, "task0", task0);
make_task(3, "task1", task1);
set_a_idt(32, (unsigned)task_timer, 0x08, 0x8E);
}
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
There are a few problems with this code. First, your task_data should contain a pointer to the task's stack_data, rather than just containing the stack_data itself. stack_data is actually the bottom of the kernel stack for that task, which means that if you keep it in the task_data, you'll be overwriting the task's name and whatever comes before it in memory as the task's kernel stack grows. That's not good. When you create a task, you need to allocate a kernel stack for it. You will have to decide how much you need to allocate -- it depends on the complexity of your kernel. At the bottom of the new stack, you will need to set up your stack_data fields, and also a ring 3 SS and ESP if you intend for the task to run in ring 3 (these will be popped by the CPU if it sees that the CS in your stack_data refers to a ring 3 code segment). After this is all set up, put a pointer to the stack_data struct in the task_data.
The next problem is that you're returning stack_data by value instead of by pointer. That creates a copy of the stack_data, which is not what you want. This will just corrupt the stack since the calling code (your ISR stub) is not prepared to deal with a struct being returned by value. It expects a pointer to a kernel stack to be returned, which is what your C code should be returning.
The next problem is the definition of your stack_data structure. Many of the fields are in the wrong order. The segment registers are ok, but the ones after it are not. Here's how it should look:
Hope this helps.
The next problem is that you're returning stack_data by value instead of by pointer. That creates a copy of the stack_data, which is not what you want. This will just corrupt the stack since the calling code (your ISR stub) is not prepared to deal with a struct being returned by value. It expects a pointer to a kernel stack to be returned, which is what your C code should be returning.
The next problem is the definition of your stack_data structure. Many of the fields are in the wrong order. The segment registers are ok, but the ones after it are not. Here's how it should look:
Code: Select all
struct stack_data
{
unsigned int gs;
unsigned int fs;
unsigned int es;
unsigned int ds;
unsigned int edi;
unsigned int esi;
unsigned int ebp;
unsigned int esp;
unsigned int ebx;
unsigned int edx;
unsigned int ecx;
unsigned int eax;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
};
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
Sorry, I missed this question on my first read through your post.GLneo wrote:in make_task()what do i need to put in the struct stack_data type ESP???
According to the way that pusha works (remember, the Intel manuals are your friend), the value of ESP that gets pushed is the value that ESP was before pushing EAX (that is, before the pusha instruction began executing). So, it should point to the eip field of the stack_data struct.
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
Re:tss on stack???
thx that helped, well at least it compiles , what now happends is it starts then does nothing, it does not crash just goes somewhere??? I followed the exicution of my kernel so if the kernel got control back it doesn't hit a CLI, and it dosent, but i did test it and it does get control back, but it sould hit a jmp $ and stop. it does get one interupt and go into my timer handler and returns then never comes back??? heres the code:
any ideas???, thx
EDIT: ok with a little bochs work i have found that es ds gs fs and stuff are at 0x10 i know this is where i sould put my user GDT and where a lot of tutorials set them to but i dont remember using 0x10, it must be on the new task stack becouse that would explain why only one switch happpens before the crash, but in my new_task code you can see the all referens 0x08, whats happening???, thx
Code: Select all
struct stack_data *schedule(struct stack_data *regs)
{
cur_task_time_equ(get_pri_time(cur_task_pri()));
front_to_end();
return rrq[front].stack;
}
struct stack_data *task_timer_c(struct stack_data *regs)
{
clock();
outport(0x20, 0x20);
if(cur_task_time() > 0)
{
dec_cur_task_time();
return regs; //<------------------It gets here only once!!!
}
else
return schedule(regs);
}
void make_task(int pri, char *name, void (*entry)())
{
unsigned int *stack;
stack = (unsigned int *)malloc(64);
*stack--;
*stack-- = 0x0202;
*stack-- = 0x08;
*stack-- = (unsigned int)entry;
*stack-- = 0; //ebp
*stack-- = 0; //esp
*stack-- = 0; //edi
*stack-- = 0; //esi
*stack-- = 0; //edx
*stack-- = 0; //ecx
*stack-- = 0; //ebx
*stack-- = 0; //eax
*stack-- = 0x8; //ds
*stack-- = 0x8; //es
*stack-- = 0x8; //fs
*stack = 0x8; //gs
strncpy(rrq[end].name, name, 32);
rrq[end].stack = (struct stack_data *)stack;
rrq[end].ss = 0x8;
rrq[end].priority = pri;
rrq[end].time = get_pri_time(pri);
end++;
}
void start_sys()
{
puts("Setting up Task0... ");
make_task(3, "task0", task0);
puts("Done.\n");
puts("Setting up Task1... ");
make_task(3, "task1", task1);
puts("Done.\n");
set_a_idt(32, (unsigned)task_timer_c, 0x08, 0x8E);
}
EDIT: ok with a little bochs work i have found that es ds gs fs and stuff are at 0x10 i know this is where i sould put my user GDT and where a lot of tutorials set them to but i dont remember using 0x10, it must be on the new task stack becouse that would explain why only one switch happpens before the crash, but in my new_task code you can see the all referens 0x08, whats happening???, thx
- Colonel Kernel
- Member
- Posts: 1437
- Joined: Tue Oct 17, 2006 6:06 pm
- Location: Vancouver, BC, Canada
- Contact:
Re:tss on stack???
First, a piece of advice. In your make_task() function, use the fields of your stack_data struct instead of just writing blindly to the malloc'd memory. You're less likely to make a mistake that way (and it would be much easier to read).
Problem #1: You still aren't setting up esp correctly. See my last post.
Problem #2: You're assigning the same selector to both CS and DS/SS/ES/etc. This is incorrect -- CS needs to point to a code segment descriptor, while the rest must point to a data segment descriptor. If your tasks are supposed to run in ring 3, then you should be putting the ring 3 code and data segment selectors in CS, DS, etc.
Also, did you remember to set up your ESP0 and SS0 fields in the TSS correctly...?
Problem #1: You still aren't setting up esp correctly. See my last post.
Problem #2: You're assigning the same selector to both CS and DS/SS/ES/etc. This is incorrect -- CS needs to point to a code segment descriptor, while the rest must point to a data segment descriptor. If your tasks are supposed to run in ring 3, then you should be putting the ring 3 code and data segment selectors in CS, DS, etc.
Also, did you remember to set up your ESP0 and SS0 fields in the TSS correctly...?
Top three reasons why my OS project died:
- Too much overtime at work
- Got married
- My brain got stuck in an infinite loop while trying to design the memory manager
Re:tss on stack???
Hi,
This won't work - the stack grows from the top down, while your doing it from the bottom down (and probably overwriting unrelated data).
You'd want to do something like:
Or, even better, something more like:
Cheers,
Brendan
Code: Select all
void make_task(int pri, char *name, void (*entry)())
{
unsigned int *stack;
stack = (unsigned int *)malloc(64);
*stack--;
*stack-- = 0x0202;
*stack-- = 0x08;
*stack-- = (unsigned int)entry;
*stack-- = 0; //ebp
*stack-- = 0; //esp
*stack-- = 0; //edi
*stack-- = 0; //esi
*stack-- = 0; //edx
*stack-- = 0; //ecx
*stack-- = 0; //ebx
*stack-- = 0; //eax
*stack-- = 0x8; //ds
*stack-- = 0x8; //es
*stack-- = 0x8; //fs
*stack = 0x8; //gs
You'd want to do something like:
Code: Select all
#define STACK_SIZE 64 // Size of stack in "unsigned integers"
void make_task(int pri, char *name, void (*entry)())
{
unsigned int *stack;
stack = (unsigned int *)malloc(STACK_SIZE * sizeof(unsigned int) );
stack += STACK_SIZE; //stack = address of top of stack
*stack--;
*stack-- = 0x0202;
*stack-- = 0x08;
*stack-- = (unsigned int)entry;
*stack-- = 0; //ebp
*stack-- = 0; //esp
*stack-- = 0; //edi
*stack-- = 0; //esi
*stack-- = 0; //edx
*stack-- = 0; //ecx
*stack-- = 0; //ebx
*stack-- = 0; //eax
*stack-- = 0x8; //ds
*stack-- = 0x8; //es
*stack-- = 0x8; //fs
*stack = 0x8; //gs
Code: Select all
#define STACK_SIZE 64*4 // Size of stack in bytes
#define KERNEL_CODE_SEG 0x0008 // Not sure if this is right
#define KERNEL_STACK_SEG 0x0010 // Not sure if this is right
#define DATA_SEG 0x0018 // Not sure if this is right
void make_task(int pri, char *name, void (*entry)())
{
void *stack;
stack_data *new_stack_data;
stack = (unsigned int *)malloc(STACK_SIZE);
stack += STACK_SIZE - sizeof(stack_data) ; //stack = address for start of stack data structure
new_stack_data = stack;
new_stack_data->gs = DATA_SEG;
new_stack_data->fs = DATA_SEG;
new_stack_data->es = DATA_SEG;
new_stack_data->ds = DATA_SEG;
new_stack_data->edi = 0;
new_stack_data->esi = 0;
new_stack_data->ebp = 0;
new_stack_data->esp = 0;
new_stack_data->ebx = 0;
new_stack_data->edx = 0;
new_stack_data->ecx = 0;
new_stack_data->eax = 0;
new_stack_data->eip = (unsigned int)entry;
new_stack_data->cs = KERNEL_CODE_SEG;
new_stack_data->eflags = 0x00000202;
strncpy(rrq[end].name, name, 32);
rrq[end].stack = new_stack_data;
rrq[end].ss = KERNEL_STACK_SEG;
rrq[end].priority = pri;
rrq[end].time = get_pri_time(pri);
end++;
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.