Page 2 of 3
Posted: Sun Apr 29, 2007 4:05 am
by pcmattman
Well ATM I've redirected my scheduler to a new work in progress to do some quick testing. I've found some interesting things, mainly that it works well (with function calls
) but after a couple of seconds I get an invalid opcode error... Any ideas why?
Posted: Sun Apr 29, 2007 4:22 am
by mystran
So you basicly call your scheduler something like below?
Code: Select all
printf("ok going to call scheduler\n");
schedule();
printf("ok I got scheduled again\n");
edit: actually that's what Voix did in the clock interrupts interrupt handler until recently, without the prints ofcourse. Now there's a slightly more advanced scheme where you aren't allowed to call scheduler from interrupt/deferred context, but instead just mark that it should be called as soon as it's allowed again...
Posted: Sun Apr 29, 2007 4:28 am
by pcmattman
No, it works via the clock interrupt. And inside the scheduler is where the text is printed.
I currently have a simple loop:
Without any multitasking (but interrupts enabled) this will throw an invalid opcode instruction. Any ideas why?
Woah... If I take out the timer install function (ie. no timer interrupt) I have no problems. Now I have to figure out
why.
Posted: Sun Apr 29, 2007 10:36 am
by frank
This is a bit lengthy so I will just attach it:
I tried to cut out as much of the code that is irrelevant to basic multitasking as possible but some may still be in there. Also I hope that it is easy to understand but if not just ask more questions.
Posted: Sun Apr 29, 2007 4:02 pm
by pcmattman
Could I have a look at the struct you use for your processes?
Edit: oops, the comments made it hard to see
Posted: Mon Apr 30, 2007 1:54 am
by pcmattman
Hey, wow, thanks! This code works really well, apart from one thing. I still can't use function calls inside functions (at least not if they get preempted by the IRQ).
For instance, the following task will execute without any problems:
Code: Select all
void task()
{
while( true )
*(unsigned short*) ( 0xB8000 + ( 79 * 2 ) ) = ( 0x1F << 8 ) | 'M';
}
But this code will throw a GPF after each process has run and it's back to the first process again:
Code: Select all
void task()
{
while( true )
{
kprintf( "a" );
}
}
It'll print out lots of 'a' characters then die with a GPF. Bochs log at time of crash:
Code: Select all
00043354249e[CPU0 ] fetch_raw_descriptor: GDT: index (a407)1480 > limit (77)
00107478000p[WGUI ] >>PANIC<< POWER button turned off.
00107478000i[SYS ] Last time is 1177919982
00107478000i[CPU0 ] protected mode
00107478000i[CPU0 ] CS.d_b = 32 bit
00107478000i[CPU0 ] SS.d_b = 32 bit
00107478000i[CPU0 ] | EAX=00000016 EBX=00157d07 ECX=000b8f28 EDX=870003d5
00107478000i[CPU0 ] | ESP=00157caf EBP=00157cb7 ESI=00000000 EDI=00000000
00107478000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af PF cf
00107478000i[CPU0 ] | SEG selector base limit G D
00107478000i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00107478000i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00107478000i[CPU0 ] | EIP=00107b0a (00107b0a)
00107478000i[CPU0 ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00107478000i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00107478000i[CPU0 ] >> jmp .+0xfffffffe (0x00107b0a) : EBFE
00107478000i[ ] restoring default signal behavior
00107478000i[CTRL ] quit_sim called with exit code 1
Any ideas why this happens?
Edit: wait. It seems to be fixed
... I don't get it.
Posted: Mon Apr 30, 2007 2:23 am
by mystran
You probably aren't restoring ESP correctly. If you inline the printing, it won't need ESP, whereas a function call obviously needs a return address, which is probably where it fails.
Posted: Mon Apr 30, 2007 2:26 am
by pcmattman
Well, I just did a test and I found that the timeslice I was giving each process was massive, and once I reduced it the problem went away (I think).
One question, how do I setup another function to be called when the task itself exits?
Posted: Mon Apr 30, 2007 2:47 am
by mystran
pcmattman wrote:Well, I just did a test and I found that the timeslice I was giving each process was massive, and once I reduced it the problem went away (I think).
One question, how do I setup another function to be called when the task itself exits?
For kernel threads or for user threads?
For kernel threads, make a function like die() or something that kills the currently running thread. It can be a bit tricky as you can't free the stack while you still need it, so easiest thing is to have a separate "killer task" or something, and have die() send it a message that the task should be killed. You don't want die() to take any parameters if you want to have it called automatically upon return.
For user threads, add a system call like _exit() which calls die() internally.
For kernel threads then, if you want them to automatically return when the function they run returns, just build a suitable stack before starting the thread: put the address for die() as the return address. As long as die() needs no parameters, this'll work, even if it sounds funny. You can then put any parameters to your thread-function below the return address, if you want.
Posted: Mon Apr 30, 2007 3:56 am
by pcmattman
I did that, it worked perfectly.
One more question I have: where do I push arguments in this code:
Code: Select all
// return addy
esp--;
*esp-- = 0x10;
*esp-- = ( unsigned int ) userstack1 + 1024; // not sure this is necessary...
*esp-- = 0x0202;
*esp-- = 0x08;
*esp-- = (unsigned int) ¨ // kills the process
/** arguments pushed here? **/
*esp-- = 0x0202;
*esp-- = 0x08;
*esp-- = (unsigned int) &task;
/** arguments pushed here? **/
/** registers pushed on stack, segments etc... **/
I think the arguments may have to be pushed after the line
Am I right?
Posted: Mon Apr 30, 2007 5:51 pm
by pcmattman
OK, I still haven't figured out the whole arguments thing... But I'm getting there
The good news is, everything that didn't work in my old multitasker is working now! Thanks everyone for all your help, especially mystran and frank - your code really helped!
As a test, I setup a thread in my kernel called 'udp:4000' as a udp server on port 4000 that sends 'woot' back to anyone who sends it data. The other thread is the system procedure that prints an 'M' in the top right corner.
It didn't crash
Posted: Mon Apr 30, 2007 7:57 pm
by frank
I am always happy to see that I could help someone. If you still can't get the argument thing down then just holler and I will see if I can help you.
Posted: Mon Apr 30, 2007 8:27 pm
by pcmattman
Yeah, I've been testing the whole argument thing and it turns out that if I run a task that takes two arguments, the two arguments I receive are the CS and EFLAGS pushed onto the stack before EIP...
Code: Select all
// base of the task
*esp-- = EFLAGS_IF | EFLAGS_IOPL0 | EFLAGS_ALWAYS1; // second?
*esp-- = 0x08; // task thinks this is first argument
*esp-- = base;
Any ideas why?
Posted: Mon Apr 30, 2007 8:36 pm
by frank
Is this a user level task and are you putting the arguments on the user stack?
Posted: Mon Apr 30, 2007 8:38 pm
by pcmattman
It's a kernel thread.
Is that why, in your code, you do this?
Code: Select all
*esp-- = 32 + 3; // user stack segment
*esp-- = (DWORD)user_stack;
Edit:
I did this:
Code: Select all
// user stack
*esp-- = 0x10;
*esp-- = ptable[i].ustack;
// base of the task
*esp-- = EFLAGS_IF | EFLAGS_IOPL0 | EFLAGS_ALWAYS1;
*esp-- = 0x08;
*esp-- = base;
And then when I got the argument from my task, it came out as 0x10? Why is it the stack segment, and not the actual data?