Multitasking - Stack/Execution problem

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:

Multitasking - Stack/Execution problem

Post by max »

Hey guys :)

Currently i am implementing my multitasking system. The other things are working so far, the paging system is disabled as long as i cant figure out my problem described here.

I basically use the method from JamesM's kernel tutorial, except that I am not storing my process list as a linked list (i dont want to use kmalloc) but as a plain list in the memory starting at MEMLOC_PROCESS_LIST. So I declare constants for each area of my kernel memory specifying the use of that specific place.

So when the multitasking is initialized, the kernel first creates its own process. Then the first switchProcess() call occurs and the kernels esp/ebp/eip are stored in the process structure.
Then a new process is spawned by calling the fork() method and from then on my switchProcess() method should switch stack and instruction pointer on each call. But there seems to be a problem when restoring my stack: qemu crashes and tells me that I tried to execute code outside RAM.

qemu: fatal: Trying to execute code outside RAM or ROM at 0x6e726c4c

I guess this could be a problem with the stack switching causing the processor to interprete a value on the stack as a function pointer and trying to execute it.

EDIT On the beginning my kernel stack is moved to 0x300000, limit at 0x250000.

Paging::createUserspace(..., ...) creates a clone of the kernel directory and makes the given range accessible to user priviledged code, but this shouldn't be a problem here because paging is not enabled.
So this is my scheduler code:

Code: Select all

extern "C" unsigned int _readEIP();

Process* processListBase = (Process*) MEMLOC_PROCESSLIST;
Process* currentProcess = 0;

int processCount = 0;
int schedulingIndex = 0;

int nextProcessId = 1;

void Scheduler::initialize() {
	asm volatile("cli");
	
	Process* process = &processListBase[processCount++];
	process->id = nextProcessId++;
	process->esp = 0;
	process->ebp = 0;
	process->eip = 0;
	process->pageDirectory = Paging::getKernelDirectory();
	process->used = true;
	currentProcess = process;
	
	asm volatile("sti");
}

int Scheduler::fork(unsigned int startAddress, unsigned int endAddress) {
	asm volatile("cli");
	
	Process* parentProcess = currentProcess;
	
	unsigned int *processDirectory = Paging::createUserspace(startAddress,
			endAddress);
	
	Process* newProcess = &processListBase[processCount++];
	newProcess->id = nextProcessId++;
	newProcess->esp = 0;
	newProcess->ebp = 0;
	newProcess->eip = 0;
	newProcess->pageDirectory = processDirectory;
	newProcess->used = true;
	
	// Entry point
	unsigned int eip = _readEIP();
	
	if (currentProcess == parentProcess) {
		unsigned int esp;
		unsigned int ebp;
		
		asm volatile("mov %%esp, %0" : "=r"(esp));
		asm volatile("mov %%ebp, %0" : "=r"(ebp));
		
		newProcess->esp = esp;
		newProcess->ebp = ebp;
		newProcess->eip = eip;
		
		asm volatile("sti");
		return newProcess->id;
	} else {
		return 0;
	}
}

Process* Scheduler::nextProcess() {
	schedulingIndex++;
	
	if (schedulingIndex > processCount - 1) {
		schedulingIndex = 0;
	}
	
	return &processListBase[schedulingIndex];
}

void Scheduler::switchProcess() {
	if (processCount > 0) {
		unsigned int eip;
		unsigned int esp;
		unsigned int ebp;
		
		asm volatile("mov %%esp, %0" : "=r"(esp));
		asm volatile("mov %%ebp, %0" : "=r"(ebp));
		
		eip = _readEIP();
		
		if (eip == 0x12345) {
			return;
		}
		
		// Store registers
		currentProcess->eip = eip;
		currentProcess->esp = esp;
		currentProcess->ebp = ebp;
		
		// Schedule
		currentProcess = nextProcess();
		
		// Load registers
		eip = currentProcess->eip;
		esp = currentProcess->esp;
		ebp = currentProcess->ebp;
		
		unsigned int currentDirectory =
				(unsigned int) currentProcess->pageDirectory;
		
		asm volatile("             \
		      cli;                 \
		      mov %0, %%ecx;       \
		      mov %1, %%esp;       \
		      mov %2, %%ebp;       \
		      mov %3, %%cr3;       \
		      mov $0x12345, %%eax; \
		      sti;                 \
		      jmp *%%ecx           "
				: :
				"r"(eip),
				"r"(esp),
				"r"(ebp),
				"r"(currentDirectory));
	}
}

int Scheduler::getPid() {
	return currentProcess->id;
}
This code is called by the kernel here:

Code: Select all

Scheduler::initialize();

int pid = Scheduler::fork(0x10000000, 0x10100000);

cout.print("Fork returned ");
cout.println(Integer::toString(pid));
cout.print("I am process ");
cout.println(Integer::toString(Scheduler::getPid()));
Can anyone figure out my problem??
I've been looking in a lot of places but I just can't see whats wrong.

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

Re: Multitasking - Stack/Execution problem

Post by Kevin »

I don't really feel like reviewing code that does things like switching tasks somewhere in the middle of C code (you'd have to bug me on IRC to talk me into that :D), but...
max wrote: asm volatile("mov %%esp, %0" : "=r"(esp));
asm volatile("mov %%ebp, %0" : "=r"(ebp));

newProcess->esp = esp;
newProcess->ebp = ebp;
newProcess->eip = eip;
...irrespective of the context, this is most certainly wrong. The register values aren't valid any more after the end of the asm statement, the compiler may change them as it likes. But even if there were still valid, wtf do you want to do with the stack pointer here? You're not seriously trying to run two tasks on a single stack?
Developer of tyndur - community OS of Lowlevel (German)
User avatar
Kazinsal
Member
Member
Posts: 559
Joined: Wed Jul 13, 2011 7:38 pm
Libera.chat IRC: Kazinsal
Location: Vancouver
Contact:

Re: Multitasking - Stack/Execution problem

Post by Kazinsal »

Kevin wrote:You're not seriously trying to run two tasks on a single stack?
Yeah, this is a majorly bad idea. Each task should really have its own stack (give it like, 64KB or something for starters), unless you have some blindingly amazing (yet incredibly hackish, no matter how elegantly put) method of preventing tasks from running all over each others stack space.

I hope you get what I'm trying to point out, max.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Multitasking - Stack/Execution problem

Post by max »

Hey Kevin and Blacklight, thank you for the answers :)

Well ive been thinking about that this could be a problem, but somehow it seems to work for JamesM? Does one of you know whats the difference there?

So if I set up a stack for each process (except the kernel process, it has its stack), i still dont get this:
When creating a new process, I first set the ebp and the esp of the task struct both to the top of my new stack. Then my switchProcess() comes in and wants to go to the next process. But how do I properly store the ebp/esp for the current process??

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

Re: Multitasking - Stack/Execution problem

Post by Kevin »

Yes, you'll need to switch the stack somewhere. I'm not familiar with how JamesM's tutorial implements this, as I said I find switching tasks inside a C function rather suspicious... Generally when entering a task, you fetch the current stack pointer from the task structure and you set esp in the same assembly block that does the actual switch. For switching back you need to store the old stack pointer value into the task structure of the task that you're leaving so that a later task switch reenters it at the right point.

In case this doesn't really help, you know which tutorial I am more familiar with. ;)
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: Multitasking - Stack/Execution problem

Post by max »

I found a solution :)

<This tutorial> helped me understand the issue.
As Kevin said, the EBP and ESP cannot simply be read where i did it and it doesnt make any sense either, because in my irq handler 0 i already push all the registers to the stack (based on <Bran's kernel development tutorial>), call my request handler and then pop them back from the stack.
So now I simply replaced the part where I call my request handler with the switching method and voila, there we go ;)
Post Reply