Page 1 of 2
ESP and EBP change not committed after return from interrupt
Posted: Mon Aug 06, 2012 12:32 pm
by DirectXtreme
Hello All;
This is my first post here and I am still a noob at OSDev so Il accept any advice/tips on posting. Anyway, I am currently trying to implement multitasking in my OS. I change tasks on the timer interrupt by changing the values of the registers stored on the stack before it is popped back off the stack. My IRQ handler looks like this:
Code: Select all
[extern] IRQ_HANDLER
IRQ_COMMON_STUB:
pusha ;push all common registers
mov ax, ds ;put data segment selector into AX
push eax ;save eax, which is value of ds
mov ax, 0x10 ;put us back into kernel mode (ring 0)
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call IRQ_HANDLER ;Call the high-level handler
pop eax ;restore eax, which contains original ds in lower 16 bits (AX)
mov ds, ax ;restore data segment selector
mov es, ax
mov fs, ax
mov gs, ax
popa ;pop all common registers
add esp, 8 ;fix stack after popping the ISR number and Error Code
sti
iret]
and IRQ_HANDLER looks like this:
Code: Select all
void IRQ_HANDLER(registers_t* regs);
If I modify any other registers e.g. regs->eax the result is committed and I can perform task switching by saving and restoring the EIP of the tasks. But if I change ESP or EBP the values of these registers are not modified and the stack of the next task is not loaded.
I am completely lost so any help at this point would be greatly appreciated.
Thank you
Re: ESP and EBP change not committed after return from inter
Posted: Mon Aug 06, 2012 2:15 pm
by xenos
EBP should be restored from the stack by popa, but the ESP value on the stack is simply discarded by popa. (IIRC - I'm too tired to look it up right now...)
Re: ESP and EBP change not committed after return from inter
Posted: Mon Aug 06, 2012 11:40 pm
by Combuster
CS/SS/EIP/ESP/EFLAGS are restored by the IRET, and never by POPA. So to change ESP in userland, your registers struct will have to have ESP stored among the 5 doublewords corresponding to the interrupt return stackframe, and not to the 8 corresponding to the pushall frame.
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 3:44 am
by DirectXtreme
XenOS wrote:EBP should be restored from the stack by popa, but the ESP value on the stack is simply discarded by popa. (IIRC - I'm too tired to look it up right now...)
I understand that but not even the new EBP is restored when the interrupt return.
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 3:47 am
by DirectXtreme
Combuster wrote:CS/SS/EIP/ESP/EFLAGS are restored by the IRET, and never by POPA. So to change ESP in userland, your registers struct will have to have ESP stored among the 5 doublewords corresponding to the interrupt return stackframe, and not to the 8 corresponding to the pushall frame.
My registers struct includes the 8 GPRs and the 5 DWoRDS popped when the interrupt handler is called.
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 4:02 am
by gerryg400
Code: Select all
void IRQ_HANDLER(registers_t* regs);
This function expects to be passed a pointer to the regs structure. Where, in your code, do you put that pointer on the stack ?
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 4:05 am
by DirectXtreme
gerryg400 wrote:Code: Select all
void IRQ_HANDLER(registers_t* regs);
This function expects to be passed a pointer to the regs structure. Where, in your code, do you put that pointer on the stack ?
Good point, I think I missed that. Stupid question, how would I do that, should I push ESP before I call IRQ_HANDLER?
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 11:01 am
by eryjus
I have some homework for you:
Research how high-level languages such as C handle pass-by-reference versus pass-by-value (hint: you are implementing one in ASM and prototyping another in C). You will need to understand the difference and how the compiler will build the stack based on which method you are using for each function. Once you understand that, translate that into what ASM code you would need to write to implement each.
By mixing ASM and C, you are now completely responsible for making sure you handle both sides in a consistent manner for each function -- meaning the compiler cannot keep you honest. This will more than like not be the only place you mix ASM and C in your OS, so it will become a necessary skill.
Re: ESP and EBP change not committed after return from inter
Posted: Tue Aug 07, 2012 12:52 pm
by DirectXtreme
Ok, I fixed some of my code by pushing ESP before the call to the IRQ_HANDLER and popping it after. This allows my
Code: Select all
void IRQ_HANDLER(registers_t* regs);
to work. But now I can still not edit EBP or ESP. My Registers struct looks as follows:
Code: Select all
struct REGISTERS
{
DWORD ds;
DWORD edi, esi, ebp, esp, ebx, edx, ecx, eax;
DWORD int_no, err_code;
DWORD eip, cs, eflags, useresp, ss;
}__attribute__((packed));
The registers struct represents what the stack will look like, as far as I can tell it will look as follows:
Code: Select all
| SS | {
| ESP |
| EFLAGS | These are popped when the interrupt is called and will be
| CS | restored on the IRET
| EIP | }
| EAX | {
| ECX | These are popped by the PUSHA and is restored by the POPA
| EDX |
| EBX |
| ESP | Modifying this has not effect as it is discard
| EBP |
| ESI |
| EDI | }
| DS | { This is pushed and popped by me }
If anyone can see anything wrong with the above please let could you let me know.
Oh and I have a really random issue, when I set regs->ecx the change is also see in regs->useresp after the interrupt returns to the new task.
Re: ESP and EBP change not committed after return from inter
Posted: Wed Aug 08, 2012 3:48 am
by DirectXtreme
Progress has been made! My IRQ handler is working properly and I can make changes to EBP and ESP in the handler. But now something weird is happening, the changes arnt committed when making changes in my task_switch function which accepts a pointer to the registers struct and is called by the IRQ handler.
Re: ESP and EBP change not committed after return from inter
Posted: Wed Aug 08, 2012 5:01 am
by Nable
> by pushing ESP before the call to the IRQ_HANDLER and popping it after
I hope that you don't pop it to ESP (functions are allowed to change their arguments).
Re: ESP and EBP change not committed after return from inter
Posted: Wed Aug 08, 2012 6:11 am
by DirectXtreme
Nable wrote:> by pushing ESP before the call to the IRQ_HANDLER and popping it after
I hope that you don't pop it to ESP (functions are allowed to change their arguments).
I am popping it back into ESP because I do not change the value. Should I rather pop it into EAX for safety reasons?
Re: ESP and EBP change not committed after return from inter
Posted: Wed Aug 08, 2012 7:20 am
by xenos
You can simply discard it (increase ESP by 4 instead of popping anything).
Re: ESP and EBP change not committed after return from inter
Posted: Fri Aug 10, 2012 12:31 am
by Nable
Should I rather pop it into EAX for safety reasons?
If I were you, I would pop to EAX or change function declaration to passing structure with regs by values instead of pointer.
Else, you can define your function as stdcall, so it will be ended with ret 4, i.e. clean argument from stack by itself.
You can simply discard it (increase ESP by 4 instead of popping anything).
AFAIK, it can be a bad idea if you care about performance: CPU's out-of-order and other execution units like when what pairs of call/ret, push/pop, interrupt/iret are balanced.
Re: ESP and EBP change not committed after return from inter
Posted: Fri Aug 10, 2012 1:55 pm
by DirectXtreme
Nable wrote:Should I rather pop it into EAX for safety reasons?
If I were you, I would pop to EAX or change function declaration to passing structure with regs by values instead of pointer.
Else, you can define your function as stdcall, so it will be ended with ret 4, i.e. clean argument from stack by itself.
You can simply discard it (increase ESP by 4 instead of popping anything).
AFAIK, it can be a bad idea if you care about performance: CPU's out-of-order and other execution units like when what pairs of call/ret, push/pop, interrupt/iret are balanced.
Nable, +1 for bringing in the aspect of performance.