Troubles implementing multitasking
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Troubles implementing multitasking
I'm having some troubles implementing supervisor multitasking...
Currently, my setup is as follows:
- A task is created:
- The memory manager allocates a new page directory, the kernel directory gets copied into the new one
- A new page gets allocated for the stack, and is mapped to a very high region (because the stack grows downwards I believe?)
- The task is initialized:
- The initial state is set so that the esp = ebp = bottom of the page that was allocated previously, eip = address of the routine to execute
- The task is enqueued.
Once the timer ticks, this happens:
- The interrupt handler is called, its argument is a pointer to the registers that were pushed by the processor
- The currently running task is updated with the state that is found on the registers, and then re-enqueued
- The new task is dequeued, the registers on the stack are updated with the state of the new task
- The handler returns
The problem is, the moment the handler returns, the OS starts executing random stuff, like launching interrupts, which makes me think it's executing instructions from random memory locations, which makes me believe I must be doing something wrong... Can anyone shed some light for me?
Currently, my setup is as follows:
- A task is created:
- The memory manager allocates a new page directory, the kernel directory gets copied into the new one
- A new page gets allocated for the stack, and is mapped to a very high region (because the stack grows downwards I believe?)
- The task is initialized:
- The initial state is set so that the esp = ebp = bottom of the page that was allocated previously, eip = address of the routine to execute
- The task is enqueued.
Once the timer ticks, this happens:
- The interrupt handler is called, its argument is a pointer to the registers that were pushed by the processor
- The currently running task is updated with the state that is found on the registers, and then re-enqueued
- The new task is dequeued, the registers on the stack are updated with the state of the new task
- The handler returns
The problem is, the moment the handler returns, the OS starts executing random stuff, like launching interrupts, which makes me think it's executing instructions from random memory locations, which makes me believe I must be doing something wrong... Can anyone shed some light for me?
- Schol-R-LEA
- Member
- Posts: 1925
- Joined: Fri Oct 27, 2006 9:42 am
- Location: Athens, GA, USA
Re: Troubles implementing multitasking
Do you have an repo on a site such as Github or Sorceforge which you could give us a link to, so we can review the code?
Or conversely, could you please post the relevant code from the interrupt handler, scheduler, and so forth? This would give us a chance to see if there is some bug in it you might have missed.
Or conversely, could you please post the relevant code from the interrupt handler, scheduler, and so forth? This would give us a chance to see if there is some bug in it you might have missed.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Re: Troubles implementing multitasking
Hi,
Cheers,
Brendan
The stack grows downwards, so you'd want "esp = top of the page that was allocated" so that the stack can grow down from the top.frabert wrote:I'm having some troubles implementing supervisor multitasking...
Currently, my setup is as follows:
- A task is created:
- The memory manager allocates a new page directory, the kernel directory gets copied into the new one
- A new page gets allocated for the stack, and is mapped to a very high region (because the stack grows downwards I believe?)
- The task is initialized:
- The initial state is set so that the esp = ebp = bottom of the page that was allocated previously, eip = address of the routine to execute
- The task is enqueued.
I'd recommend using a debugger (e.g. single-step one instruction at a time in Bochs) to watch exactly what the CPU does at each step, starting from a breakpoint at the start of the low-level task switch code.frabert wrote:The problem is, the moment the handler returns, the OS starts executing random stuff, like launching interrupts, which makes me think it's executing instructions from random memory locations, which makes me believe I must be doing something wrong... Can anyone shed some light for me?
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
Here. I will now try and see stepping the code as Brendan suggested with BochsSchol-R-LEA wrote:Do you have an repo on a site such as Github or Sorceforge which you could give us a link to, so we can review the code?
EDIT: I was able to fix the first of the issues, https://github.com/frabert/toy-os/blob/ ... ng.cpp#L82 should read (uintptr_t)m_func, not (uintptr_t)&m_func, though now I am able to only run two tasks, and I have to put an infinite loop at the end of the functions I call, but that's just because I am not setting up the stack with a proper return pad I guess. After a while the functions throw a page fault, so maybe now it's messing with the paging...
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
Hey I managed to get it almost working!
But I have another problem now. Stepping through the code instruction by instruction, I noticed that when executing "iretd", the esp register does not get updated at the end of the interrupt handler... What's the correct way of setting a new stack for the tasks?
But I have another problem now. Stepping through the code instruction by instruction, I noticed that when executing "iretd", the esp register does not get updated at the end of the interrupt handler... What's the correct way of setting a new stack for the tasks?
- Attachments
-
- Screenshot from 2018-08-04 09-10-55.png (7.46 KiB) Viewed 4210 times
- TheCool1Kevin
- Posts: 24
- Joined: Fri Oct 14, 2016 7:37 pm
- Location: Canada
- Contact:
Re: Troubles implementing multitasking
About your esp, iret does not change it. It's up to your OS to update the esp on a context switch. Don't forget the TSS too.
It's your OS so do whatever makes sense (but be warned, bad design decisions come back to haunt you).
In my IRQ handler, I had in order:And so I set up the stack like:
Then to switch tasks, I switch the ESP/page directories and jump to "irq_return" and let the cpu handle the rest. And so to run the task, I set up the task's stack and queue it, where the scheduler then changes the stack and the cpu pops the return address and runs the code. Not sure if it's THE way, but it works and so far nothing bad has happened.
Looking at your code, you do almost the same thing, except that your "switchTasks" works differently. It can't be that bad.
It's your OS so do whatever makes sense (but be warned, bad design decisions come back to haunt you).
In my IRQ handler, I had in order:
Code: Select all
irq_return:
pop gs
pop fs
pop es
pop ds
popa
add esp, 8 ; Error code and other shenanigans
iret
Code: Select all
uint32_t *stack = (uint32_t *)malloc(4096);
stack = (uint32_t *)((int)stack + 4096);
stack -= 16; // Padding
*--stack = 0x10; // ss
*--stack = 0x00; // esp
*--stack = 0x202; // eflags
*--stack = 0x08; // cs
*--stack = entry_point; // eip
*--stack = 0x00; // err_code
*--stack = 0x00; // int_no
/* pushad */
*--stack = 0xCAFEBABE; // eax
*--stack = 0x00; // ecx
*--stack = 0x00; // edx
*--stack = 0x00; // ebx
*--stack = 0x00; // esp_dummy
*--stack = 0x00; // ebp
*--stack = 0x00; // esi
*--stack = 0x00; // edi
/* Push segment registers */
*--stack = 0x10; // ds
*--stack = 0x10; // es
*--stack = 0x10; // fs
*--stack = 0x10; // gs
proc->thread.regs.registers = (void *)stack; // Top of stack
Looking at your code, you do almost the same thing, except that your "switchTasks" works differently. It can't be that bad.
LiquiDOS, my weird hobbyist OS.
"Strive for progress, not perfection" - Anonymous
"Strive for progress, not perfection" - Anonymous
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
What I can't wrap my head around is how to change the esp _after_ the iret, since if I change it before it would break the iret behavior, right?
But, if I can't do it before, how am I going to tell the newly created task where to look for its fresh stack?
But, if I can't do it before, how am I going to tell the newly created task where to look for its fresh stack?
Re: Troubles implementing multitasking
After a task switch where do you want the iret to return to? Think about it.
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
Well, to the eip of the resumed task as it was saved on suspension, right?
Re: Troubles implementing multitasking
Right. Which stack is that going to be on?
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
Its own, the one I have allocated when I initially created the task (in my case it's a whole page for simplicity)
Re: Troubles implementing multitasking
So you change the stack pointer before the iret, so that it points to the correct stack (where the return address was pushed last time the task relinquished control).
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: Troubles implementing multitasking
But if I change the esp before the iret, what is iret going to pop into the eflags and all of that? Or am I supposed to save all of that inside the task's stack too?
Re: Troubles implementing multitasking
All that was saved when you called the interrupt that originally switched away from the task. Restoring cr3 and the stack pointer (plus all the other registers you have saved) returns the task to the state it was in before you switched.
- TheCool1Kevin
- Posts: 24
- Joined: Fri Oct 14, 2016 7:37 pm
- Location: Canada
- Contact:
Re: Troubles implementing multitasking
yes. The interrupt handler should save all the registers (segment registers included); then when you switch tasks, you point esp to the new task's stack, and the interrupt handler should popa and pop the segment registers too. When iret is executed, the CPU pops the EIP and off you go.
Also I should note that after you switch stacks, you should jump directly to your popa/iret instructions to avoid breaking the stack.
CR3 does not get restored. Neither does ESP.Restoring cr3 and the stack pointer (plus all the other registers you have saved)
Also I should note that after you switch stacks, you should jump directly to your popa/iret instructions to avoid breaking the stack.
LiquiDOS, my weird hobbyist OS.
"Strive for progress, not perfection" - Anonymous
"Strive for progress, not perfection" - Anonymous