Page 1 of 2
page fault processing
Posted: Thu Jul 02, 2020 5:31 pm
by mrjbom
Hi.
I wrote a handler for the page fault exception, but suddenly found that the error code I get from the stack is not always the same in the same cases(I restarted the kernel and the error code was different, including the P RW US bits).
I got the value from the stack using an assembler insert in the handler in C(Yes, I know how stupid this is, I didn't take into account the stack change after starting the C function).
Now I was trying to copy the error code to some general-purpose register in an assembler handler.
It looked like this:
Code: Select all
page_fault:
pusha
pop eax
call page_fault_handler
popa
iret
But this caused the system to crash or the exception didn't work at all.
Now I don't understand how to get the error code value...
How?
Re: page fault processing
Posted: Thu Jul 02, 2020 5:48 pm
by Octocontrabass
Re: page fault processing
Posted: Thu Jul 02, 2020 11:47 pm
by iansjack
So you push all registers, pop what was in edi into edx, call some routine then do an interrupt return without restoring the stack. Under these conditions a crash is expected behaviour.
Re: page fault processing
Posted: Fri Jul 03, 2020 1:12 am
by mrjbom
popa was always present, so it should be in the handler before calling the C function.
And pop eax according to my idea should save the error code in eax.
Re: page fault processing
Posted: Fri Jul 03, 2020 1:23 am
by mrjbom
iansjack wrote:So you push all registers, pop what was in edi into edx, call some routine then do an interrupt return without restoring the stack. Under these conditions a crash is expected behaviour.
Code: Select all
page_fault:
;save error code from stack
pop eax
push eax
pusha
call page_fault_handler
popa
iret
void page_fault_exception() {
serial_printf("page_fault_exception!\n");
uint32_t error_code = 0;
__asm__ volatile ("mov %%eax, %0" : "=r"(error_code));
. . .
}
Now I move the value from the stack to eax and immediately return it, everything seems to be working as it should now.
Will this code be correct?
Re: page fault processing
Posted: Fri Jul 03, 2020 2:52 am
by iansjack
The code is nearly correct, but it clobbers the eax register, and it doesn't remove the error code from the stack.
If you wish to preserve the value of eax you should push all registers then read the value of the error code directly from the appropriate position on the stack. Remember that the stack is just a series of memory locations so you can read/write its values directly (with an offset[%esp] instruction) instead of using pop/push. Also, remember to remove the error code from the stack before you return from the interrupt.
Re: page fault processing
Posted: Fri Jul 03, 2020 4:55 am
by mrjbom
iansjack wrote:The code is nearly correct, but it clobbers the eax register, and it doesn't remove the error code from the stack.
If you wish to preserve the value of eax you should push all registers then read the value of the error code directly from the appropriate position on the stack. Remember that the stack is just a series of memory locations so you can read/write its values directly (with an offset[%esp] instruction) instead of using pop/push. Also, remember to remove the error code from the stack before you return from the interrupt.
I changed the code this way:
Code: Select all
page_fault:
;save error code from stack to eax
pop eax
;return error code to stack
push eax
;save all 32bit registers
pushad
call page_fault_handler
;return all 32bit registers
popad
iretd
(in all interrupts, I also replaced pusha, popa, iret with functions for bit mode(added d))
I don't fully understand how I can remove the error code from the stack at the end of the handler... if I make pop %eax to delete before popad, then popad will restore the esp and it will reference the wrong place...
Re: page fault processing
Posted: Fri Jul 03, 2020 5:01 am
by nexos
My ISR stub looks like:
Code: Select all
cli
push byte 14
pushad
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, HalIsrHandler
call eax
pop eax
popad
add esp, 8
sti
iretd
Note that it passes the structure as a pointer. It cleans up the error code and interrupt number by adding 8 to esp. The D isn't necessary. NASM will translate. (It won't in 64 bit mode, but that is irrelevant.) I include the D for verbosity's purposes.
Re: page fault processing
Posted: Fri Jul 03, 2020 5:12 am
by mrjbom
nexos wrote:My ISR stub looks like:
Code: Select all
cli
push byte 14
pushad
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, HalIsrHandler
call eax
pop eax
popad
add esp, 8
sti
iretd
Note that it passes the structure as a pointer. It cleans up the error code and interrupt number by adding 8 to esp. The D isn't necessary. NASM will translate. (It won't in 64 bit mode, but that is irrelevant.) I include the D for verbosity's purposes.
Exactly, I forgot that it is desirable to disable interrupts while the interrupt handler is running.
Now my handler looks like this, I only clear the error code with add esp, 4.
Is that correct?
Code: Select all
page_fault:
cli
;save error code from stack to eax
pop eax
;return error code to stack
push eax
;save all 32bit registers
pushad
call page_fault_handler
;return all 32bit registers
popad
;delete error code from stack
add esp, 4
sti
iretd
Re: page fault processing
Posted: Fri Jul 03, 2020 5:19 am
by nexos
Yes, that looks good. Are using the registers as a structure in your C handler? If so, then it is good idea to pass the structure by pointer. Right before you call the function, you would do something like this
Re: page fault processing
Posted: Fri Jul 03, 2020 5:25 am
by mrjbom
nexos wrote:Yes, that looks good. Are using the registers as a structure in your C handler? If so, then it is good idea to pass the structure by pointer. Right before you call the function, you would do something like this
What do you mean by "structure"? If you're talking about C structures, no, I don't use them in the C handler.
Re: page fault processing
Posted: Fri Jul 03, 2020 8:47 am
by Octocontrabass
mrjbom wrote:Is that correct?
No. You're still clobbering EAX. If you want to pass the error code to the function you're calling in a register, you need to read it from the stack after PUSHAD, using an instruction like "mov eax, [esp+32]". (How are you passing arguments to a C function in registers? The System V ABI uses the stack to pass arguments.)
You're also using CLI/STI, which is unnecessary. If you want to disable interrupts, set up your IDT so that this entry uses an interrupt gate.
You can do that in one instruction:
Re: page fault processing
Posted: Fri Jul 03, 2020 3:16 pm
by mrjbom
Octocontrabass wrote:mrjbom wrote:Is that correct?
No. You're still clobbering EAX. If you want to pass the error code to the function you're calling in a register, you need to read it from the stack after PUSHAD, using an instruction like "mov eax, [esp+32]"
Yes, exactly...
I changed the code like this:
But now the exception is looped, even though it shouldn't be... Did I do something wrong? After all, now registers are not damaged.
Code: Select all
page_fault:
cli
;save all 32bit registers
pushad
;save error code from stack to eax
mov eax, [esp + 4]
call page_fault_handler
;return all 32bit registers
popad
;delete error code from stack
add esp, 4
sti
iretd
Re: page fault processing
Posted: Fri Jul 03, 2020 3:41 pm
by nexos
push eax directly before the calling the function and all should be well. The ABI requires parameters to be passed on the stack.
Re: page fault processing
Posted: Fri Jul 03, 2020 3:47 pm
by mrjbom
nexos wrote:push eax directly before the calling the function and all should be well. The ABI requires parameters to be passed on the stack.
But my ะก function doesn't have any parameters... Why do this?
If I push eax before calling the function, the behavior is still not correct...
Code: Select all
page_fault:
cli
pushad
;save error code from stack to eax
mov eax, [esp + 4]
push eax
call page_fault_handler
;return all 32bit registers
popad
;delete error code from stack
add esp, 4
sti
iretd