Page 1 of 1
Real machine restart after IRETD instruction execution
Posted: Mon Dec 16, 2013 6:23 pm
by yee1
Hey friends,
Today I am here with new problem
I've built task scheduler and it's working fine when running at Bochs.
Problem is running it at real machine / virtualbox.
At virtualbox screen freezes after IRETD instruction is executed.
At real machine IRETD from task code makes it restart.
I have checked TSS while task code is executed and selector of previous task is correct (offset 0x0, 2 byte data).
I have no idea what may be reason of it. I am sure that IRETD causes real machine and virtual box restart.
I am using nasm and I call new task this way:
Code: Select all
call dword 0x58:0x0 ;0x58 task selector from GDT
Task code is like that:
Code: Select all
task1:
;some code here
iretd
jmp task1
I was doing some "tricks" like switching task from timer's interrupt or main task, but all returns same result - reebot.
Re: Real machine restart after IRETD instruction execution
Posted: Mon Dec 16, 2013 6:37 pm
by Brendan
Hi,
yee1 wrote:I've built task scheduler and it's working fine when running at Bochs.
Problem is running it at real machine / virtualbox.
At virtualbox screen freezes after IRETD instruction is executed.
At real machine IRETD from task code makes it restart.
"Restart" normally means "triple fault", which happens when there's an exception and the CPU can't start the exception handler (and then can't start the double fault exception handler either).
yee1 wrote:I am using nasm and I call new task this way:
Code: Select all
call dword 0x58:0x0 ;0x58 task selector from GDT
Task code is like that:
Code: Select all
task1:
;some code here
iretd
jmp task1
I have no idea why this didn't crash on Bochs too. The correct way to return is with the "retf" instruction.
Cheers,
Brendan
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 2:37 am
by yee1
Brendan wrote:Hi,
yee1 wrote:I've built task scheduler and it's working fine when running at Bochs.
Problem is running it at real machine / virtualbox.
At virtualbox screen freezes after IRETD instruction is executed.
At real machine IRETD from task code makes it restart.
"Restart" normally means "triple fault", which happens when there's an exception and the CPU can't start the exception handler (and then can't start the double fault exception handler either).
yee1 wrote:I am using nasm and I call new task this way:
Code: Select all
call dword 0x58:0x0 ;0x58 task selector from GDT
Task code is like that:
Code: Select all
task1:
;some code here
iretd
jmp task1
I have no idea why this didn't crash on Bochs too. The correct way to return is with the "retf" instruction.
Cheers,
Brendan
I changed IRETD to RETF and it's not working at real machine / virtual box and now not working also with Bochs.
Bochs returns:
00166160841e[CPU0 ] fetch_raw_descriptor: LDTR.valid=0
00166160841e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00166160844e[CPU0 ] fetch_raw_descriptor: LDTR.valid=0
00166160844e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00166160847e[CPU0 ] fetch_raw_descriptor: LDTR.valid=0
00166160847e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00166160850e[CPU0 ] fetch_raw_descriptor: LDTR.valid=0
00166160850e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00166160850e[CPU0 ] interrupt(): gate.type(2) != {5,6,7,14,15}
I have found that it should be IRETD instruction to back from interrupt gate and from task, so where is a problem ?
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 4:38 am
by AJ
Hi,
1. What does IRETD do (and what does it expect on the stack)?
2. What does RETF do (and what does it expect on the stack)?
3. What did your original far CALL do (and what does it push on to the stack)?
4. What is on your stack just before the end of task1? You can very easily investigate this on Bochs.
Cheers,
Adam
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 4:45 am
by iansjack
I have found that it should be IRETD instruction to back from interrupt gate and from task, so where is a problem ?
I think you must have misinterpreted what you have read. I don't believe that you will find a reliable reference telling you that you should CALL a task and then IRETD from it. On the other hand, you will certainly find references telling you (correctly) that one way to switch tasks is via an IRETD.
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 5:02 am
by yee1
AJ wrote:Hi,
1. What does IRETD do (and what does it expect on the stack)?
2. What does RETF do (and what does it expect on the stack)?
3. What did your original far CALL do (and what does it push on to the stack)?
4. What is on your stack just before the end of task1? You can very easily investigate this on Bochs.
Cheers,
Adam
iansjack wrote:I have found that it should be IRETD instruction to back from interrupt gate and from task, so where is a problem ?
I think you must have misinterpreted what you have read. I don't believe that you will find a reliable reference telling you that you should CALL a task and then IRETD from it. On the other hand, you will certainly find references telling you (correctly) that one way to switch tasks is via an IRETD.
Guys see this
http://www.embedded.com/design/prototyp ... Processors
Although tasks can't be reentrant, they can be nested. If you switched tasks through a FAR CALL (as opposed to a FAR JMP), the new task is considered to be nested within the old task. The new task will have its Back Link field filled in with a pointer to the old task's TSS; this happens automatically. Both tasks will also have their "busy bit" set, so they can't be called again. The processor also sets the NT (nested task) flag in its EFLAGS register. This is essentially a "valid bit" that says the Back Link field is now officially binding.
With NT set and the back link updated, the processor knows this task is a child of another task. An IRET (interrupt return) instruction will now un-nest the tasks. Normally, an IRET just pops a return address off the stack and performs a normal interrupt return. When NT is set, however, the processor performs a task switch, clears the NT bit, and follows the back link to the parent task. Pretty nice work for a collection of transistors, huh?
By the way, a normal RET instruction won't un-nest tasks, even if they were nested by CALL instructions. Only an IRET will do this.
Using retf instruction also causes this exception at real machine (at bochs works fine lol).
What may be other reason of it ?
I was also checking my stack for tasks and it seems to be fine.
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 6:35 am
by yee1
I modified code to use far jump
Where i dynamically set dword value of selector and problem still happens.
I have 3 tasks at the moment and my scheduler do it this way:
1. switch to task number 1 => OK (working fine)
2. switch to task number 2 => OK (working fine)
3. switch to task number 3 => OK (working fine)
4. switch to task number 1 => exception occurs
What may be reason of this exception ?
It seems like I am doing same mistake with call and jmp instruction. With jmp there is a problem with switching again to task that was switched to before (2nd time). With call there is a problem with IRETD instruction or as you said RETF, but dunno what to think about it - I was trying RETF and exception occurs
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 8:12 am
by Brendan
Hi,
Ok, so far we have 3 problems:
- The assembler you're using is so hideously unusable that you had to resort to not using assembly and using machine code instead (the "db 0xEA, ..." stuff)
- You were using an "interrupt return" to return from a call that was never an interrupt; and this problem was masking the symptoms of another problem
- Something causes the "LDTR.valid=0" error. My best guess is that you never did "LTR" anywhere in your initialisation code, so when the CPU tries to do the very first task switch it can't store the previous task's state (before loading the next task's state).
Cheers,
Brendan
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 8:27 am
by Brendan
Hi,
Just an extra question (I'm curious) - why are you using "call far" for task switching in the first place? This should only be done when you need to switch to one task for a little while and then always switch back to the original task; which is almost never the case. To be perfectly honest; I can't think of any valid use for "call far" for task switching.
For scheduling, typically some kernel code gets running (due to an IRQ or system call) in the context of the currently running task, and (for whatever reason) the kernel decides to do a task switch; so the kernel code does a "jmp far" from the task that was running to the new task without expecting that new task to return to the original task; and eventually the kernel decides to do another task switch, and does another "jmp far" to the next task, then the next, and so on ("jumping" from task to task to task while never expecting any task to "return" to the previous task).
Also note that (for hardware task switching) the difference between "call far" and "jmp far" is how the CPU handles the "busy" flag in the TSS. For "jumping" the previous task's "busy" flag is cleared (so you can "jump" back to it whenever you like); and for "calling" the previous task's "busy" flag is left set to make sure that the only possible way to switch back to it is via. a "retf" (to guard against recursive calls).
Cheers,
Brendan
Re: Real machine restart after IRETD instruction execution
Posted: Tue Dec 17, 2013 4:27 pm
by yee1
Brendan wrote:Hi,
Just an extra question (I'm curious) - why are you using "call far" for task switching in the first place? This should only be done when you need to switch to one task for a little while and then always switch back to the original task; which is almost never the case. To be perfectly honest; I can't think of any valid use for "call far" for task switching.
For scheduling, typically some kernel code gets running (due to an IRQ or system call) in the context of the currently running task, and (for whatever reason) the kernel decides to do a task switch; so the kernel code does a "jmp far" from the task that was running to the new task without expecting that new task to return to the original task; and eventually the kernel decides to do another task switch, and does another "jmp far" to the next task, then the next, and so on ("jumping" from task to task to task while never expecting any task to "return" to the previous task).
Also note that (for hardware task switching) the difference between "call far" and "jmp far" is how the CPU handles the "busy" flag in the TSS. For "jumping" the previous task's "busy" flag is cleared (so you can "jump" back to it whenever you like); and for "calling" the previous task's "busy" flag is left set to make sure that the only possible way to switch back to it is via. a "retf" (to guard against recursive calls).
Cheers,
Brendan
Ok, I did some code's cleanup and it seems to be working... almost perfect.
Let's have a scenario with 3 tasks:
Code: Select all
irq0_interr_handler:
inc word [currentTask]
mov ax, word [currentTask]
cmp ax, 1
je runTask1
cmp ax, 2
je runTask2
; run task 3 code here
jmp dword 0x40:0x0
jmp taskRunEnd
runTask1:
jmp dword 0x30:0x0
jmp taskRunEnd
runTask2:
jmp dword 0x38:0x0
taskRunEnd:
mov al, 60h
out 20h, al
iretd
task1:
task1loop:
;task1's code
jmp task1loop
task2:
task2loop:
;task2's code
jmp task2loop
task3:
task3loop:
;task3's code
jmp task3loop
Above code also includes save registers but i haven't pasted it there.
A problem here is that when running
jmp dword instruction 1st time it go to task and next instruction is not executed. Next times
jmp dword is executed and task is switched but next instruction is also executed (next = jmp task1loop / task2loop / task3loop ). These times value to port 20h is saved and iretd executed.
Why is it happening in this way ?
This code is working in Bochs but it's not working with virtual box / real machine. What may be cause of it ?
Thank you for helping me.
Re: Real machine restart after IRETD instruction execution
Posted: Wed Dec 18, 2013 12:32 am
by Brendan
Hi,
yee1 wrote:This code is working in Bochs but it's not working with virtual box / real machine. What may be cause of it ?
It shouldn't be working in Bochs either - you have to send the EOI to the PIC before you do the task switch.
Also note that it's cleaner to do an indirect far jump ("jmp far [currentTSS]") instead of "if(1) jmp 1; elseif(2) jmp 2; elseif(3) jmp 3;...".
Finally (eventually) you want this split into 3 pieces - the IRQ handler, a "reschedule" function and a "goto_task" function. For an example and explanation see
this post.
Cheers,
Brendan
Re: Real machine restart after IRETD instruction execution
Posted: Wed Dec 18, 2013 3:16 am
by yee1
Brendan wrote:Hi,
yee1 wrote:This code is working in Bochs but it's not working with virtual box / real machine. What may be cause of it ?
It shouldn't be working in Bochs either - you have to send the EOI to the PIC before you do the task switch.
Also note that it's cleaner to do an indirect far jump ("jmp far [currentTSS]") instead of "if(1) jmp 1; elseif(2) jmp 2; elseif(3) jmp 3;...".
Finally (eventually) you want this split into 3 pieces - the IRQ handler, a "reschedule" function and a "goto_task" function. For an example and explanation see
this post.
Cheers,
Brendan
About EIO - Yes, you're right. I was writing this code from memory and forgot about this, it's in my code. But my problem is about task switching and running instructions after task switch (jmp dword) instruction.
Ok the, it seems that you're talking about this code:
Code: Select all
section .data
currentTSSfarPointer:
dd 0 ;Note: CPU ignores the "offset" part, so there's not much point having it..
currentTSS:
dw 0x38
section .text
;Note: IRQ0 *must* be an "interrupt gate" (and can not be a "trap gate")
irq0_interrupt_service:
push eax
;send eoi signal (end of interrupt)
mov al, 0x20
out 0x20, al
call reschedule
pop eax
iretd
;Find task to run and switch to it
reschedule:
push eax
movzx eax,[currentTSS]
add eax,,8 ;eax = next TSS to switch to
cmp eax,0x38 ;Is it too high?
jbe .l1 ; no
mov eax,0x30 ; yes, wrap around to first TSS
call gotoTask
pop eax
ret
;Switch to a specific task
;
;Input
; ax = TSS for task to switch to
gotoTask:
cmp [currentTSS],ax ;Is this task currently running?
je .done ; yes, do *not* attempt to switch (will cause GPF)
mov [currentTSS],ax
jmp far [currentTSSfarPointer] ;WARNING: Task switch and not a JMP (execution continues after the jump)
.done:
ret
I don't understand this line of code
Code: Select all
jmp far [currentTSSfarPointer] ;WARNING: Task switch and not a JMP (execution continues after the jump)
Why will it continue execution of next instructions after
jmp far [currentTSSfarPointer] is executed ?
Well I know there should be values to be pop'ed from stack by iret instruction, but how it's possible that after far jump next instruction won't be executed of jumped task ? When irq interrupt is done at stack it has pushed 12 bytes (eip/cs/eflags), right ? But eip/cs/eflags are saved data of task where irq was executed. When iret will be executed it would back task that it was executed from, right ?
Well you have used "jmp far" instruction, i suppose it for tasm or fasm. I write my code in nasm and i am using "jmp dword" instruction. Is it wrong ? Opcodes of both instructions are different.
Thank you.
Re: Real machine restart after IRETD instruction execution
Posted: Wed Dec 18, 2013 6:43 am
by Brendan
Hi,
yee1 wrote:I don't understand this line of code
Code: Select all
jmp far [currentTSSfarPointer] ;WARNING: Task switch and not a JMP (execution continues after the jump)
Why will it continue execution of next instructions after
jmp far [currentTSSfarPointer] is executed ?
The current task will continue at the next instruction after the current task gets CPU time again (after the task switch, and after something causes a task switch back to this task).
yee1 wrote:Well I know there should be values to be pop'ed from stack by iret instruction, but how it's possible that after far jump next instruction won't be executed of jumped task ? When irq interrupt is done at stack it has pushed 12 bytes (eip/cs/eflags), right ? But eip/cs/eflags are saved data of task where irq was executed. When iret will be executed it would back task that it was executed from, right ?
The IRET doesn't get executed until after something switches back to the current task.
yee1 wrote:Well you have used "jmp far" instruction, i suppose it for tasm or fasm. I write my code in nasm and i am using "jmp dword" instruction. Is it wrong ? Opcodes of both instructions are different.
The "jmp far [currentTSSfarPointer]" is for NASM. It's an indirect jump ("jump to the address stored at currentTSSfarPointer") rather than a direct jump ("jump to currentTSSfarPointer").
Cheers,
Brendan
Re: Real machine restart after IRETD instruction execution
Posted: Thu Dec 19, 2013 5:39 am
by yee1
Hey Brendan,
I'd like to thank you for your help. Thank to your tips I have solved my problem, thank you!
Chocolates for you