[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: intercepting an exception handler.
On Thu, 5 Sep 2002 12:10:57 +0530
"Mohit Kalra" <kmohit@in.ibm.com> wrote:
>Now my question is : can't we skip the generic handler part and
>directly write the address of the c handler into the IDT table? If i
>do this the user program gives me an OOPs. In step 3 above, if we
>save the low and high offsets of the C handler, things should work
>right?
I'll try to show why you obtain an oops.
For doing it I will suppose an exception raises at some time. Suppose
CPU is executing an istruction and so %cs and %eip identify logical
address of the instruction the current process will execute next.
When CPU has to execute it, CPU control unity checks whether an
interrupt or an excpetion has occurred while executing last
instruction.
If so there's a first part of exception handling which is done by
CPU control unit :
1- Determines the vector which identifies exception (or interrupt) by
reading PIC's pins D0-D7 which are connected to data bus.
2- Reads IDT i-th entry (through %idtr register) and there it finds
Segment Selector (normally __KERNEL_CS) from which it's possible to
identify segment in which handler code will be found.
3- Now we know segment and so we can access to the right segment
through GDT (register %gdtr). Obviously Offset field in IDT entry
will allow to find handler code.
4- Some controls. First of all if CPL stored in the two least
significant bitsof %cs is lower than DPL stored of the Segment
Descriptor unity control raises a "General Protection" exception
(because handler could NOT run with lower privileges than process that
raised it). For programmed exceptions (for example syscalls) unity
control checks if CPL with DPL stored in IDT entry. If DPL is lower
than CPL unity control raises a "General Protection" exception
(because it means there's an attemp from User Mode to access a IDT
entry with DPL = 0 and so reserved to kernel).
5- If a change of privilege level is taking place it's necessary to
save in TSS %ss and %esp and store in these registers new prilivege
stack address.
6- If a fault occurred, save in %cs and %eip address of the
instruction which raised it so that it can be executed again (think
about a Page Fault for example).
7- Save %eflags, %cs and %eip in the stack.
8- If exception carries a hardware error code save it in the stack.
9- Load %cs and %eip with values found in Segment Selector and Offset
fields in IDT entry thus realizing jump to handler.
Now let's see int3 just as example.
ENTRY(int3)
pushl $0 <- this is done only if control unit didn'it. For
int3, control unit doesn't do it so it's done
through an explicit pushl.(So in the stack you
have just one hardware error code)
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
pushl $0 is pushing hardware error code and then there's pushing
SYMBOL_NAME(do_int3).
Let's see now error_code which is used by all exceptions ("Device not
available" not).
error_code:
pushl %ds
pushl %eax
xorl %eax,%eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx
decl %eax # eax = -1
pushl %ecx
pushl %ebx <- All registers that might be used
by C handler are saved in the
stack. The reason why eax = -1
is less clear but it is useful
for system call handling.
cld <- Clearing DF flag in %eflags for
allowing autoincrements on %edi
and %esi registers
movl %es,%ecx
movl ORIG_EAX(%esp), %esi # get the error code
(which is stored at offset
ORIG_EAX = 0x24 from %esp)
movl ES(%esp), %edi # get the function address
(which is stored at offset
ES = 0x20 from %esp)
movl %eax, ORIG_EAX(%esp)
movl %ecx, ES(%esp)
movl %esp,%edx
pushl %esi # push the error code
pushl %edx # push the pt_regs pointer
movl $(__KERNEL_DS),%edx
movl %edx,%ds
movl %edx,%es
GET_CURRENT(%ebx) <- current process descriptor stored in %ebx
call *%edi <- here handler (whose address is in %esi) is called
but look at the stack!
In the stack handler finds :
-return address of the instruction to be
executed after C handler
-error hardware code
-SYMBOL_NAME
-registers saved
-the stack address of the saved User Mode
registers
-error hardware code
<---%esp points here
addl $8,%esp <- the last two objects in stack are popped
jmp ret_from_exception and the ret_from_exception is called
ret_from_exception:
movl EFLAGS(%esp),%eax # mix EFLAGS and CS
movb CS(%esp),%al
testl $(VM_MASK | 3),%eax #return to VM86 mode or non-supervisor?
jne ret_from_sys_call
jmp restore_all
ALIGN
If it's not a system call simply restore all saved registers.
#define RESTORE_ALL \
popl %ebx; \
popl %ecx; \
popl %edx; \
popl %esi; \
popl %edi; \
popl %ebp; \
popl %eax; \
1: popl %ds; \
2: popl %es; \
addl $4,%esp; \
3: iret; \
(it was not reported fixup code)
The last 'function' called is iret. The first thing it makes is
resuming %cs, %eip and %eflags saved by control unit.
If a error hardware code was pushed, it has to be popped before iret
execution.
After this long talking probably you would be able to know what's
wrong in what you did. If you put C handler address in IDT entry the
first part (done by control unit) will be done and so logical address
of next instruction to be executed is saved....but when you resume it?
If you insert your C function you will never execute
ret_from_exception. Then probably you could have other problems since
you're destroying hardware process context but you saved nothing....
So if you look well you could have a lot of problems doing as you did!
Regards,
Angelo Dell'Aera 'buffer'
<buffer@users.sourceforge.net>
--
Kernelnewbies: Help each other learn about the Linux kernel.
Archive: http://mail.nl.linux.org/kernelnewbies/
FAQ: http://kernelnewbies.org/faq/