Page fault when switching from one context to another

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
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Page fault when switching from one context to another

Post by max »

Hey guys :)

I've now implemented my multitasking so far, that it works with a single process running in userspace, but once I add another one I get a page fault.

My initial page directory is set up like this:
- 0x00000000 - 0x000100000 the lower memory, mapped for kernel access
- 0xC0000000 - 0xFFC000000 the kernel memory area, mapped with kernel access. I'm already creating all tables here to make sure any change to the kernel area happens in all contexts.
- 0xFFC00000 - end, the recursively mapped directory

Now, I'm setting up two processes doing the following
- Cloning all kernel-access dir entries to a new directory entry, and set entry 1023 to the new dir
- Creating a kernel stack in that new directory at 0xBFFFF000, putting the initial registers on it
- Creating a user stack at 0xBFFFE000, for the process itself
- Creating some test loop at 0x60000000, and setting EIP to this location

That all works fine so far, but only if I have just one process. The problem occurs in my scheduler:

Code: Select all

Context* Scheduler::handle(Context* context) {
	if (current == 0) {
		current = first;
	} else {
		current->context = context;

		current = current->next;
		if (current == 0) {
			current = first;
		}
	}

	Logger::println("preparing switch from context: %h to context: %h", context,
			current->context);

	Logger::println("  setting page directory: %h", current->pageDirectory);
	PagingUtil::switchToDirectory(current->pageDirectory);

	Logger::println("  setting ESP0: %h", current->esp0);
	GDTInitializer::setTssEsp0(current->esp0);

	return current->context;
}
this leads to the following output after re-enabling interrupts with two processes created in advance:

Code: Select all

preparing switch from context: 0xC0007F78 to context: 0xBFFFFFB8
  setting page directory: 0x3FFF3000
  setting ESP0: 0xC0000000
returning to POPs and IRET   // this message comes from the request dispatcher
preparing switch from context: 0xBFFFFFB8 to context: 0xBFFFFFB8
  setting page directory: 0x3FFF9000
[panic] exception: page fault
  eip:  0x00000001    eflags: 0x00000046
  eax:  0x3FFF9000       ebx: 0x00000000
  ecx:  0x00000000       edx: 0x00000000
  intr: 0x0000000E     error: 0x00000000
So it happens exactly when I'm trying to switch from the page directory of the first process to the page directory of the second process. In the process->pageDirectory there is the physical address of the page directory which is a clone of the kernel space + kernel stack + user stack + test code..

Does one of you have an idea what might be going wrong?

Thanks in advance :)
Greets, Max
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by Kevin »

max wrote:- 0x00000000 - 0x000100000 the lower memory, mapped for kernel access
- Cloning all kernel-access dir entries to a new directory entry, and set entry 1023 to the new dir
What do you do with entry 0? Isn't it part kernel and part user?
preparing switch from context: 0xBFFFFFB8 to context: 0xBFFFFFB8
Can this be right? I suspect that this "context" is what contains the register state etc. of the task. Two tasks sharing the same state is... interesting.
[panic] exception: page fault
eip: 0x00000001 eflags: 0x00000046
eax: 0x3FFF9000 ebx: 0x00000000
ecx: 0x00000000 edx: 0x00000000
intr: 0x0000000E error: 0x00000000
You should extend your register dump to include at least esp, ebp, cr2 and cr3, probably also segment registers. Anyway, this eip looks rather unlikely. Your debugging should probably focus on what made the CPU jump to 0x1 (probably iret from a corrupted stack; may be related to your context sharing).
So it happens exactly when I'm trying to switch from the page directory of the first process to the page directory of the second process.
That is, the last instruction that works correctly is a "mov %eax, %cr3"? Where does your eip=0x1 come from then? Or is your exception handler buggy and this is not the real eip of the fault?
Developer of tyndur - community OS of Lowlevel (German)
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by max »

Kevin wrote:Can this be right? I suspect that this "context" is what contains the register state etc. of the task. Two tasks sharing the same state is... interesting.
The "context" is just a pointer to the register state on the stack. So the kernel stack in essence looks like this (the values are just dummies):

Code: Select all

0xBFFFF000                      "Context"          0xC0000000
^                               ^                  ^
[ 0 0 0 0 0 0 0 0 0 0 ... 0 0 0 43 54 2A 3D 2D ... ]
I am switching the page directory before popping the registers off the stack - and because every process has its own kernel stack mapped to 0xBFFFF000, the location of the registers is still the same, but is translated to different physical memory.
Kevin wrote:You should extend your register dump to include at least esp, ebp, cr2 and cr3, probably also segment registers. Anyway, this eip looks rather unlikely. Your debugging should probably focus on what made the CPU jump to 0x1 (probably iret from a corrupted stack; may be related to your context sharing).
Yes I'll do this, I'll add the other registers to my post.
That is, the last instruction that works correctly is a "mov %eax, %cr3"? Where does your eip=0x1 come from then? Or is your exception handler buggy and this is not the real eip of the fault?
Hmm I'll try to find out with gdb what exactly leads to that jump and then add the info here, too. ;)
dansmahajan
Member
Member
Posts: 62
Joined: Mon Jan 07, 2013 10:38 am

Re: Page fault when switching from one context to another

Post by dansmahajan »

max wrote:

Code: Select all

Logger::println("  setting ESP0: %h", current->esp0);
	GDTInitializer::setTssEsp0(current->esp0);

	return current->context;
}
Well i think since you setup the stack in C function and then returning from it makes stack confuse where to jump.
You can debug by putting some breakpoints and calling the debugger to dump registers/memory.
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by Kevin »

max wrote:I am switching the page directory before popping the registers off the stack - and because every process has its own kernel stack mapped to 0xBFFFF000, the location of the registers is still the same, but is translated to different physical memory.
So you're effectively switching the stack in the middle of a C function, which continues to use its old stack pointer? That is, both stacks must have the same number of elements on them at the very least? How do you initialise a new stack correctly so that it can start working in the middle of this C function?

Initially, when I just saw that you switch the active page directory in the middle of the function, I was just going to call it scary - but the more I think about it, how can this be supposed to work?
Developer of tyndur - community OS of Lowlevel (German)
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by max »

Kevin wrote:So you're effectively switching the stack in the middle of a C function, which continues to use its old stack pointer? That is, both stacks must have the same number of elements on them at the very least? How do you initialise a new stack correctly so that it can start working in the middle of this C function?

Initially, when I just saw that you switch the active page directory in the middle of the function, I was just going to call it scary - but the more I think about it, how can this be supposed to work?
Uhm.. yeah.. now that I think about it that really seems like a bad thing (TM) :D i didnt really consider that the current stack is actually switched..
i just cant figure out where to put the register state and how to restore them then on an IRET.. also which stack is used during interrupt handling? as far as i understood, the method im doing is the same as in most tutorials/infopages out there.. but i seem to be missing an important point :/ could you give me an idea on how to do it right? just a rough outline like:
kernel starts -> creates first process, puts initial register values to ??? -> timer interrupt kicks in -> current registers are pushed to ??? - scheduler does ??? -> next tasks registers are popped... and so on..

Thank you :)
Kevin
Member
Member
Posts: 1071
Joined: Sun Feb 01, 2009 6:11 am
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by Kevin »

The code of the Lowlevel tutorial that I seem to remember you followed has one kernel stack per task. It saves the userspace register values to that stack and then calls into the interrupt handlers, which still run on the kernel stack of the interrupted task. The interrupt handler (which may or may not invoke the scheduler in order to switch the task) returns a new stack pointer, and only then the stack is switched.

At this point, the whole contents of the stack is exactly defined and consists only of the register dump. As this is the only point at which a stack switch occurs, it is not only the contents of the active stack, but all inactive stacks are in the same state (they became inactive exactly at that point). This and that all registers can be discarded at this point (they will be immediately reloaded with the userspace values of the active task) is the reason why switching the stack at this one specific point is valid.

Does this answer your question?
Developer of tyndur - community OS of Lowlevel (German)
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Page fault when switching from one context to another

Post by max »

Kevin wrote:The code of the Lowlevel tutorial that I seem to remember you followed has one kernel stack per task. It saves the userspace register values to that stack and then calls into the interrupt handlers, which still run on the kernel stack of the interrupted task. The interrupt handler (which may or may not invoke the scheduler in order to switch the task) returns a new stack pointer, and only then the stack is switched.

At this point, the whole contents of the stack is exactly defined and consists only of the register dump. As this is the only point at which a stack switch occurs, it is not only the contents of the active stack, but all inactive stacks are in the same state (they became inactive exactly at that point). This and that all registers can be discarded at this point (they will be immediately reloaded with the userspace values of the active task) is the reason why switching the stack at this one specific point is valid.

Does this answer your question?
Yes this helped me. It's really not a good idea to switch the current by switching the context :lol:
Now I've mapped my kernel stacks to the kernel area, giving each one a different address, and voila. ;)
Post Reply