My question
1) Is it good to do multi tasking by switching register values of 2
Tasks
as an example
void switch(regs *r)
{
memcpy(current_task.r,r,sizeof(regs));
memcpy(r,next_task.r,sizeof(regs));
}
The switch() is called from timer interrupt handler in C code.
(task struct has a pointer to regs.as a member)
Please suggest me If it is good then what should I do to pass argument
to the task.
Please refere the task.cpp file under code.google.com/p/nanoos/
Thanks
Ashok.
Yes.
You only need to switch nonvolatile regs in this. Volatile regs are spoiled by calling the SwitchAway function anyway, according to the calling convention.
But, on hardware interrupt prolog, you need to save _all_ registers.
--
Maxim S. Shatskih
Windows DDK MVP
ma...@storagecraft.com
http://www.storagecraft.com
Yes I am doing the same, But assume I want to pass parameters to the
task, How I will do it?
I tried to populate a stack and assign it to esp, The contenet of the
stack is as follows
top> argument
top-1> entry
top-2---sizeof(regs) > register values
regs->eip =entry
but the Invalid OPCODE exception happens
Ashok.
any more thought/Help???
Well I followed the geek-os and tryed the task_switch mechanism from
it with the specified
stack structure still I am unable to pass the parameter to the task
function.
I am inspecting why it is happening, In the mean time I also need some
thought or scheme
how to achieve will be help full.
Thanks
Ashok.
------------------
http://code.google.com/p/nanoos/
It's probably more normal to just switch a pointer. Save and restore
would work with the current pointer.
> The switch() is called from timer interrupt handler in C code.
> (task struct has a pointer to regs.as a member)
>
> Please suggest me If it is good then what should I do to pass argument
> to the task.
You only pass arguments when you start a task, not when switching
between tasks. For switch() code and some good explanations see
http://webcast.berkeley.edu/media/common/rss/Computer_Science_162__001_Fall_2009_Video__webcast.rss
and look at lectures 4 and 5. I think lecture 5 contains the actual
thread switching you need and how to approach it.
James
It looks to me like he's just switching register sets for each task. I
think he needs to switch stacks too. Doesn't he? Otherwise the stack gets
corrupted, or the data on the stack gets out-of-sequence for the appropriate
task.
RP
Aha! I can't remember where the discussion ended but weren't you
previously arguing for using *one* stack for multiple tasks?
(Rhetorical question. No need to reply!)
In fact I'm not clear what the OP's plans are but copying register
values using memcpy doesn't seem right. Registers should, IMHO, be
saved when switching out of the old task. Many of them are saved
anyway when switching to the interrupt handler. Then they can be
restored on return. It's just that the return may go back to a
different task or thread.
That just leaves the problem of how to initialise the thread saved
state and any parameters that the OP mentioned. IIRC the video
lectures I linked to show a good way to deal with that.
Either way I agree with what you mentioned - that the stack is part of
the per-task state that needs to be saved and restored.
James
RP,
I guess You are right, But i am creating a stack and my esp in the reg
set points to that.
after i populate the stack, i switch the registers.
I guess by some means I'm corrupting the stack.
Das
-----------------------
http://code.google.com/p/nanoos/
I will look into those tutorials.
Well copying the register set is just not sufficient, but it is ok for
context saving and restoring. But it never
Ok for returning from interrupt( IRET ). Hence i'm now in my current
code following the stack change.
e.g
*--stack_top -> args
*--stack_top-> entry
*--stack_top-> 0 // should be a thread/task exit
after this I am copying all the register set
now when I am switching task I now change only the task pointer i
already have stored.
Might be I am doing things in Wrong way.
If I don't succeed then I will definitely poke the group again... :)
Das
--------------
http://code.google.com/p/nanoos/
These articles might be a help to you...
http://www.embedded.com/columns/technicalinsights/55301875?_requestid=231663
Managing Tasks on x86 Processors
http://www.embedded.com/columns/significantbits/22104365?_requestid=280086
Helping underprivileged code
http://www.embedded.com/columns/significantbits/18400795?_requestid=287915
Taming the x86 beast
In Helping underprivileged code, it says:
"
It's amazing to me that all of this happens in hardware. There's not a
lick of code involved in this process, just a lot of data tables.
Granted, it takes the processor about 100 clock cycles (less than a
microsecond at 500MHz) to do all this, but it's entirely automatic and
mechanical. For some small real-time kernels, privilege protection and
task switching is all they do; now that work can be done entirely in
hardware.
Where code goes, so goes the stack
When you change privilege levels, you change the addressable domain of
your program. For example, when your code is running at privilege
level 2 (PL2), you can access PL2 and PL3 data segments and a PL2
stack segment. If you make a successful call through a call gate to a
PL1 code segment, your privilege level increases to PL1 and you can
access PL1, PL2, and PL3 data segments. But what about your stack?
When you change privilege levels, your stack changes automatically.
Your old SS segment and stack pointer are abandoned and replaced with
new ones that correspond to the new, higher privilege level. Where
does this new stack come from? Hmmm, I feel a new data structure
coming on.
Believe it or not, there's still one more magical data structure you
need to create if you're going to use privilege protection on x86
processors. This new one is called the task state segment (TSS). We'll
save the gory details for another day, but in brief, the TSS includes
four different sets of stack pointers, one for each privilege level.
And, of course, you get to (read: have to) define where each of these
stacks will go. Obviously, the PL1 stack should be in a PL1 stack
segment, and so forth. You might never need all four of these stacks;
your code might never call a subroutine through a PL1 call gate. But
leaving these stacks undefined is a really bad idea. You'll have an
awfully tough time figuring out why your code suffered a sudden stack
failure after a routine function call.
Defensive programming
Sharp-eyed readers will notice that we glossed over one of the bit
fields in the call gate. Bits 32 through 36 define the number of 32-
bit parameters that will be passed from the calling routine into the
called routine. The chip will automatically copy this many bytes
(times four) from your stack to the called routine's stack.
After every FAR CALL there should be a matching FAR RET (return)
instruction that pops the correct number of parameters off the stack.
Just to make things tricky, call gates define the number of parameters
as 32-bit words, while the FAR RET instruction counts bytes. Be sure
to multiply by four before punching your return ticket.
This automatic parameter passing makes it awkward to write a routine
that accepts a variable number of arguments. The call gate will copy a
fixed number of bytes onto the called routine's stack, and the FAR RET
must remove exactly that many at the end. A single function will
either need several call gates, each with a different parameter count
(and with a matching exit point), or it will have to be coded for a
worst-case payload.
If 31 words (124 bytes) isn't enough space, you might want to pass
parameters by reference, rather than by value. In other words, push a
pointer to a data structure rather than the data itself. After all,
the called routine can access any data memory that the calling routine
could possibly have used, by virtue of its higher privilege level.
Each FAR RET executes one final step to aid security. Just before
control returns to the old, less-privileged code, the data segment
registers DS, ES, FS, and GS are all checked to see if the called
procedure might have left indexes to more privileged segments in them.
If so, the offending segment registers are zeroed. This keeps high-
level procedures that are sloppy with their segment registers from
unwittingly giving less-privileged procedures access to memory that
would otherwise be off-limits.
Oh, and you can't return values on the stack. Both the caller's stack
and the called routine's stack will shrink by the number of bytes
specified in the call gate and the FAR RET instruction, respectively.
When the caller regains control, it looks as though no parameters were
ever on the stack. (Besides, the two routines use physically separate
stacks.) You've got to return values in registers.
Finally, you should save and restore all segment registers in your
called functions. This is more to protect the caller than the callee.
If the called function changes any of the data segment registers to
point to segments the caller doesn't have permission to access, the
processor will zero those segment registers to prevent you from
passing on ownership of a privileged segment. The caller might regain
control with one or more of its segment registers cleared, which can
be a miserable bug to track. If the caller doesn't use, say, the GS
register frequently it can be hard to track down why it generates
General Protection Faults later on.
"
I'm not sure what NANOOS strategy is, but it seems to point in the
above direction.
According to what he says above, the caller can specify and pass
parameters on the stack to the new task throught the callgate, but can
only expect values returned in the registers.
However, I think if a shared descriptor is used as a 'common data
block'.. well the caller&callee would need to be at the same privilege
level.
The way I understand privilege, less privilege can call in to more
privilege 'conforming' code, and more privilege code can dereference
out to the less privileged data.
I'm not sure about your memcpy design. Even with all ring0 sections,
you need the correct descriptors as part of the function call, likely
they are not 'in common'. TSS's are separate memory spaces, no?
hth,
Steve
That's probably not what the OP wants to attempt, ever...
Yes, I said that could be done. I wasn't arguing for it... A stack per
process or task is probably a better choice for most.
A single stack can be used for multiple processes/tasks. It's not optimal.
It has "lock up" issues. The stack can't always be unrolled with multiple
users. E.g., a stackframe can't be removed until a loop exits, so the
previously running app is halted... So, some app's will "halt" until
another app has exited or popped off certain stackframes. IIRC, it also
required a bit of logic in the epilog to unroll the stack correctly. I.e.,
a task switch and/or unrolling of the stack need to selectively occur in the
epilog.
Rod Pemberton
> I'm not sure what NANOOS strategy is, but it seems to point in the
> above direction.
>
Currently There is
1)only kernel(ring 0) no user tasks.
2)No TSS based task switching. (only software based task switching)
3)No plans for user land soon
4)When Kernel is done Then User land (ring 3) will be designed.
5)initially one-to-one thread mapping
6)many-to-many
7) I cant think of ( as it is a hobby kernel sorry :) )
> According to what he says above, the caller can specify and pass
> parameters on the stack to the new task throught the callgate, but can
> only expect values returned in the registers.
>
only a single task/thread parameter passed by reference in kernel
thread
> However, I think if a shared descriptor is used as a 'common data
> block'.. well the caller&callee would need to be at the same privilege
> level.
Yes they are as all code belong to kernel only.
>
> The way I understand privilege, less privilege can call in to more
> privilege 'conforming' code, and more privilege code can dereference
> out to the less privileged data.
>
> I'm not sure about your memcpy design. Even with all ring0 sections,
> you need the correct descriptors as part of the function call, likely
> they are not 'in common'. TSS's are separate memory spaces, no?
>
> hth,
>
Yes definitely it helped me ;) As i am learning how to implement multi
tasking.
> Steve
Das
--------------------
http://code.google.com/p/nanoos/
Well Now the multitasking is working, that is i can create different
tasks can pass parameters to it, switch between tasks on timer
interrupts, can remove a task if it finished.
Now I will try to implement parent task *waiting* for a child task and
similar associated functions.
Well again: is it necessary to implement fork() and similar things as
in Unix clones?
I mean in fork child inherit all properties of parent do I need to
implement similar things?
\these questions may sound odd :) but ...
Das
--------------------
http://code.google.com/p/nanoos/
If you intend to support some of that Unix functionality or more or
less easily port existing applications to your OS, you'll need fork()
and the co. However, if you don't, the details of the API and
implementation are up to you.
> I mean in fork child inherit all properties of parent do I need to
> implement similar things?
Think of what you actually need. You may not need to inherit
everything, but there are some useful properties like the user and
access rights. If you want to share the memory between the two
processes via copy-on-write (COW) and some other things, then you want
some more. If you just want to execute another task w/o much implicit
data sharing, you don't need to implement many things.
> \these questions may sound odd :) but ...
We all learn by trial and error, asking questions and receiving
advice.
Alex
Well No clone, till now it is in my mind that it should be as simple
as possible.
With concept of programming the hardware.
1) it should be able to provide a mechanism for drivers,
2) atleast some games ( tic-tac-toe, if i can be too ambitious then a
PACMAN )
3) ping like network utility
This might interest you for ideas, esp. section 1.4 Real Time Monitor.
http://www.cpm.z80.de/manuals/mpm86sg.pdf
My understanding is this method follows the DEC VMS school of
multitasking.
hth,
Steve
Hi Steve,
Nice Document. Well ... let me see how much efficient I am to put few
of these.
das.
------------------
http://code.google.com/p/nanoos/