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
You are missing some of the required knowledge.

Code: Select all

  pusha
  pop eax
What does this code do?

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
Octocontrabass wrote:You are missing some of the required knowledge.

Code: Select all

  pusha
  pop eax
What does this code do?
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

Code: Select all

mov eax, esp
push eax

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

Code: Select all

mov eax, esp
push eax
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.
nexos wrote:

Code: Select all

mov eax, esp
push eax
You can do that in one instruction:

Code: Select all

push esp

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