Hi.
I'm trying to get my task switching code to continue executing previously interrupted threads running in ring 3.
To do this, I have to put the correct return address on the stack at this point, but I can't find it in the user thread stack.
When a thread is interrupted(by an interrupt from PIT), the return address is stored in its stack, but when the user thread is interrupted, its stack is replaced with tss.esp0, but I save the stack of the interrupted thread using the parameters of the interrupt handler. Everything is fine here, the address is correct.
I tried examining the user stack for the presence of a return address(in my case, it should be equal to one of the two known values), but I can't find it there.
I also tried to study the TSS.esp0 stack, there is no return address there either.
As a result: there are no return addresses in the TSS.esp0 stack or in the saved user stack.
(I tried artificially pushing an address into the stack that looks like a return address to me, but it doesn't help, so I may be wrong about the lack of a return address in the stack, I don't understand which one is correct)
Cannot resume a previously interrupted user process
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Cannot resume a previously interrupted user process
Wouldn't it be easier to switch kernel stacks? It's not enough to change the user ESP, you must also restore all of the other registers for the user. Since you probably put those on the stack when an interrupt happens, switching stacks will switch everything at once.
Re: Cannot resume a previously interrupted user process
I don't fully understand what you're talking about.Octocontrabass wrote:Wouldn't it be easier to switch kernel stacks? It's not enough to change the user ESP, you must also restore all of the other registers for the user. Since you probably put those on the stack when an interrupt happens, switching stacks will switch everything at once.
I switch to the user stack and try to use the return address stored there for iret...
And I'm not good at restoring all general-purpose registers, and I don't think it's necessary(I switch between kernel tasks without restoring general-purpose registers, and everything works correctly)
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Cannot resume a previously interrupted user process
The return address for whatever called your function is stored on the kernel stack, so all you need to do is switch kernel stacks and you'll return to whichever function you interrupted when you switched tasks. Then that function can simply IRET to user mode with the values on the kernel stack, without changing any of them.mrjbom wrote:I don't fully understand what you're talking about.
I switch to the user stack and try to use the return address stored there for iret...
The only time that won't work is in a new thread with an empty stack. When you create a new thread, you can either handle it specially when you switch to it, or fill the new kernel stack with appropriate return addresses before adding it to the scheduler's queue.
Interrupt handlers must save and restore all registers. Called functions must save and restore the callee-saved registers. If you don't do this, you will have problems in the future.mrjbom wrote:And I'm not good at restoring all registers, and I don't think it's necessary(I switch between kernel tasks without restoring General-purpose registers, and everything works correctly)
Re: Cannot resume a previously interrupted user process
The most basic kernel stack? What do I create at the very beginning when I start the kernel?Octocontrabass wrote:mrjbom wrote:The return address for whatever called your function is stored on the kernel stack
I'll investigate it tomorrow and try to find the return address there...
-
- Member
- Posts: 5568
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Cannot resume a previously interrupted user process
What does "the most basic" mean?mrjbom wrote:The most basic kernel stack?
Re: Cannot resume a previously interrupted user process
have no idea.Octocontrabass wrote:What does "the most basic" mean?mrjbom wrote:The most basic kernel stack?
I want to know what you mean by "kernel stack"
Re: Cannot resume a previously interrupted user process
You are talking about the function being called and its return address.Octocontrabass wrote:The return address for whatever called your function is stored on the kernel stack, so all you need to do is switch kernel stacks and you'll return to whichever function you interrupted when you switched tasks.mrjbom wrote:I don't fully understand what you're talking about.
I switch to the user stack and try to use the return address stored there for iret...
But in my case, the user function is interrupted by an interrupt and I try to find the return address in its stack, but it is not there.
When kernel threads are interrupted, I can find their return address, but in the case of user threads, I can't find the return address. I tried looking at the saved user stack and the tss.esp0 stack, but there is no return address anywhere.
Re: Cannot resume a previously interrupted user process
Perhaps you should re-read Chapter 6 of Volume 3 of the Intel Programmer's Manual to be sure that you understand how interrupts work, particularly when there is a change of privilege level. In brief, the processor switches to the kernel stack then pushes the return address (and other information) to that stack. So that is where you should be looking for the return address - although there's no need to look for it.
When you change context you switch to the kernel stack for that process (each process has its own user and kernel stacks) so the iret will return you to the point at which the switched-to process was interrupted. When you create a new process you need to create a new kernel stack (and a user stack) for it, and fake the information on it so that an iret will transfer control to the first instruction of the process.
Read that chapter, and read it again, until you are confident that you fully understand how interrupts work. They are an essential component of your OS. It would help to single-step through an interrupt call in a debugger, watching what happens to the stack(s).
And, yes - you need to save and restore all registers used by a process (which means - at least - all the general-purpose registers) when doing a context switch. This is not like a function call where you have "scratch" registers that needn't be saved. The cpu needs to return to the exact state that it was in when it was last running the process.
When you change context you switch to the kernel stack for that process (each process has its own user and kernel stacks) so the iret will return you to the point at which the switched-to process was interrupted. When you create a new process you need to create a new kernel stack (and a user stack) for it, and fake the information on it so that an iret will transfer control to the first instruction of the process.
Read that chapter, and read it again, until you are confident that you fully understand how interrupts work. They are an essential component of your OS. It would help to single-step through an interrupt call in a debugger, watching what happens to the stack(s).
And, yes - you need to save and restore all registers used by a process (which means - at least - all the general-purpose registers) when doing a context switch. This is not like a function call where you have "scratch" registers that needn't be saved. The cpu needs to return to the exact state that it was in when it was last running the process.