page fault processing

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.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

page fault processing

Post 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?
Octocontrabass
Member
Member
Posts: 5885
Joined: Mon Mar 25, 2013 7:01 pm

Re: page fault processing

Post by Octocontrabass »

You are missing some of the required knowledge.

Code: Select all

  pusha
  pop eax
What does this code do?
User avatar
iansjack
Member
Member
Posts: 4834
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: page fault processing

Post 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.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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.
Last edited by mrjbom on Fri Jul 03, 2020 4:11 pm, edited 1 time in total.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

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

Re: page fault processing

Post 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.
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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...
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: page fault processing

Post 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.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: page fault processing

Post 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
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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.
Octocontrabass
Member
Member
Posts: 5885
Joined: Mon Mar 25, 2013 7:01 pm

Re: page fault processing

Post 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
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: page fault processing

Post 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.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
mrjbom
Member
Member
Posts: 322
Joined: Sun Jul 21, 2019 7:34 am

Re: page fault processing

Post 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
Post Reply