Wanting the best of both worlds...

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Wanting the best of both worlds...

Post by AJ »

Hello All,

As you probably know (and are fed up with), I have recently implemented the multitasking part of my kernel.

Originally, I had it set up (with stack-switching), so that at the very base of the stack, I added an address and CS of a 'clearing up' function. This way, if a process returned from its main function, the task was automatically terminated.

However, I have just implemented support for the argc and argv parameters to the main function. This means that these are now pushed to the new task's stack above the terminating function and so the processor will actually try to ' ret ' using argc and argv instead of the true return address.

How do people cope with this situation in their os's? Do I simply have to hope that I can catch this with a gpf or pf, or does every user-space process have to be 'good' and use exit()? I was really hoping to elegantly cope with poorly written user-space code.

Any suggestions appreciated,
Adam
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

The common solution lies in what is called the Runtime. This is a piece of code designed to prepare a program for execution, and shutting it down afterwards.

What it may look like: (asm):

Code: Select all

start: call runtime_start
       push argv
       push argc
       call main
       add esp, 8
       call runtime_end
       call exit_process
this is then linked with the program and executed first when the program has started

If you ever wondered what crt0.o was about, it does just that.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Fantastic, thank you!

Because I am still executing tasks directly from my kernel binary rather than loading userspace from disk (I just haven't got that far yet), I hadn't looked at this.

This was exactly what I was looking for though - seems like I am at the stage where I need to look at the loading services my kernel supports, and at creating a standard library....

Cheers,
Adam
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

I'd just like to add an update for anyone else stuck at this point, to let you know how it was solved (as per combuster):

How a new task's stack looks before the task is run (in pseudo-code!):

Code: Select all

//stack base
BLANK  //space for an exit code
0x0202 //eflags for the task 'cleanup' function
CS       //cs for 'cleanup' function.
EIP      //eip for 'cleanup' function.

argv    //arguments for new task's 'main' function
argc
EIP      //eip for new task's entry point

0x0202  //eflags for the runtime stub (will also be used for the new task)
CS        //cs for runtime stub (which will be used for the new task)
EIP       //eip for the runtime stub
GP        //GP registers for new task (retrieved via popa)
DS
ES
FS
GS       //segments for new task
//esp - stack top
At this point, my task switching routine (in asm) pops all the segments and gp registers. It then performs an iret, landing it at the start of this runtime stub:

Code: Select all

global _mt_runtime
;c prototype - mt_runtime(void *entry);
_mt_runtime
     add  esp, 4    ;puts the stack at the point of argc and argv
     call [esp-4]   ;call the main function
     add  esp, 8    ;remove padding: argc and argv
iret                    ;if the stack is properly set up, returns to the cleanup function.
The cleanup function (written in c) can then remove the task from the scheduler, free memory etc...

Presently, I am just running kernel-mode code which is loaded from the kernel binary itself, but everything is running very smoothly when started with this method, whether it is a 'tsr' type program or whether the main function by moving the exit code on to the start of the stack in the reserved space, it should also be possible to get the exit code in the cleanup function.

Thanks again,
Adam
Post Reply