diff options
Diffstat (limited to 'Documentation/x86/exception-tables.rst')
-rw-r--r-- | Documentation/x86/exception-tables.rst | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/Documentation/x86/exception-tables.rst b/Documentation/x86/exception-tables.rst index ed6d4b0cf62c..efde1fef4fbd 100644 --- a/Documentation/x86/exception-tables.rst +++ b/Documentation/x86/exception-tables.rst @@ -32,14 +32,14 @@ Whenever the kernel tries to access an address that is currently not accessible, the CPU generates a page fault exception and calls the page fault handler:: - void do_page_fault(struct pt_regs *regs, unsigned long error_code) + void exc_page_fault(struct pt_regs *regs, unsigned long error_code) in arch/x86/mm/fault.c. The parameters on the stack are set up by the low level assembly glue in arch/x86/entry/entry_32.S. The parameter regs is a pointer to the saved registers on the stack, error_code contains a reason code for the exception. -do_page_fault first obtains the unaccessible address from the CPU +exc_page_fault() first obtains the inaccessible address from the CPU control register CR2. If the address is within the virtual address space of the process, the fault probably occurred, because the page was not swapped in, write protected or something similar. However, @@ -57,10 +57,10 @@ Where does fixup point to? Since we jump to the contents of fixup, fixup obviously points to executable code. This code is hidden inside the user access macros. -I have picked the get_user macro defined in arch/x86/include/asm/uaccess.h +I have picked the get_user() macro defined in arch/x86/include/asm/uaccess.h as an example. The definition is somewhat hard to follow, so let's peek at the code generated by the preprocessor and the compiler. I selected -the get_user call in drivers/char/sysrq.c for a detailed examination. +the get_user() call in drivers/char/sysrq.c for a detailed examination. The original code in sysrq.c line 587:: @@ -257,6 +257,9 @@ the fault, in our case the actual value is c0199ff5: the original assembly code: > 3: movl $-14,%eax and linked in vmlinux : > c0199ff5 <.fixup+10b5> movl $0xfffffff2,%eax +If the fixup was able to handle the exception, control flow may be returned +to the instruction after the one that triggered the fault, ie. local label 2b. + The assembly code:: > .section __ex_table,"a" @@ -278,12 +281,15 @@ vma occurs? > c017e7a5 <do_con_write+e1> movb (%ebx),%dl #. MMU generates exception -#. CPU calls do_page_fault -#. do page fault calls search_exception_table (regs->eip == c017e7a5); -#. search_exception_table looks up the address c017e7a5 in the +#. CPU calls exc_page_fault() +#. exc_page_fault() calls do_user_addr_fault() +#. do_user_addr_fault() calls kernelmode_fixup_or_oops() +#. kernelmode_fixup_or_oops() calls fixup_exception() (regs->eip == c017e7a5); +#. fixup_exception() calls search_exception_tables() +#. search_exception_tables() looks up the address c017e7a5 in the exception table (i.e. the contents of the ELF section __ex_table) and returns the address of the associated fault handle code c0199ff5. -#. do_page_fault modifies its own return address to point to the fault +#. fixup_exception() modifies its own return address to point to the fault handle code and returns. #. execution continues in the fault handling code. #. a) EAX becomes -EFAULT (== -14) @@ -295,9 +301,9 @@ The steps 8a to 8c in a certain way emulate the faulting instruction. That's it, mostly. If you look at our example, you might ask why we set EAX to -EFAULT in the exception handler code. Well, the -get_user macro actually returns a value: 0, if the user access was +get_user() macro actually returns a value: 0, if the user access was successful, -EFAULT on failure. Our original code did not test this -return value, however the inline assembly code in get_user tries to +return value, however the inline assembly code in get_user() tries to return -EFAULT. GCC selected EAX to return this value. NOTE: @@ -337,10 +343,15 @@ pointer which points to one of: entry->insn. It is used to distinguish page faults from machine check. -3) ``int ex_handler_ext(const struct exception_table_entry *fixup)`` - This case is used for uaccess_err ... we need to set a flag - in the task structure. Before the handler functions existed this - case was handled by adding a large offset to the fixup to tag - it as special. - More functions can easily be added. + +CONFIG_BUILDTIME_TABLE_SORT allows the __ex_table section to be sorted post +link of the kernel image, via a host utility scripts/sorttable. It will set the +symbol main_extable_sort_needed to 0, avoiding sorting the __ex_table section +at boot time. With the exception table sorted, at runtime when an exception +occurs we can quickly lookup the __ex_table entry via binary search. + +This is not just a boot time optimization, some architectures require this +table to be sorted in order to handle exceptions relatively early in the boot +process. For example, i386 makes use of this form of exception handling before +paging support is even enabled! |