OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Apr 25, 2024 5:58 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: page fault processing
PostPosted: Thu Jul 02, 2020 5:31 pm 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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?


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Thu Jul 02, 2020 5:48 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5145
You are missing some of the required knowledge.

Code:
  pusha
  pop eax

What does this code do?


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Thu Jul 02, 2020 11:47 pm 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4595
Location: Chichester, UK
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.


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 1:12 am 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
Octocontrabass wrote:
You are missing some of the required knowledge.
Code:
  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.

Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 1:23 am 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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?


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 2:52 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4595
Location: Chichester, UK
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.


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 4:55 am 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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...


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 5:01 am 
Offline
Member
Member

Joined: Tue Feb 18, 2020 3:29 pm
Posts: 1071
My ISR stub looks like:
Code:
    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


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 5:12 am 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
nexos wrote:
My ISR stub looks like:
Code:
    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:
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


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 5:19 am 
Offline
Member
Member

Joined: Tue Feb 18, 2020 3:29 pm
Posts: 1071
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:
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


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 5:25 am 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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.


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 8:47 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5145
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:
mov eax, esp
push eax

You can do that in one instruction:
Code:
push esp


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 3:16 pm 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 3:41 pm 
Offline
Member
Member

Joined: Tue Feb 18, 2020 3:29 pm
Posts: 1071
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


Top
 Profile  
 
 Post subject: Re: page fault processing
PostPosted: Fri Jul 03, 2020 3:47 pm 
Offline
Member
Member
User avatar

Joined: Sun Jul 21, 2019 7:34 am
Posts: 300
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:
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


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Majestic-12 [Bot], rdos and 235 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group