Real machine restart after IRETD instruction execution

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
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Real machine restart after IRETD instruction execution

Post by yee1 »

Hey friends,
Today I am here with new problem [-o<

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.
Last edited by yee1 on Tue Dec 17, 2013 3:22 am, edited 1 time in total.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post 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 ?
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
User avatar
iansjack
Member
Member
Posts: 4711
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Real machine restart after IRETD instruction execution

Post 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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post 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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post by yee1 »

I modified code to use far jump

Code: Select all

db 0xEA
dd 0x0000
selector dw 0x0
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 ;)
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post 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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post 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.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Real machine restart after IRETD instruction execution

Post 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
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.
yee1
Member
Member
Posts: 42
Joined: Sun Feb 10, 2013 4:02 pm

Re: Real machine restart after IRETD instruction execution

Post 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 ;)

Image
Post Reply