f9072c166b
The SET_INTERRUPT_HANDLER macro defines and registers an interrupt handler. It outputs a trampoline for the interrupt handler using a block of inline assembly, and the address of that trampoline is what is actually placed in the IDT. That trampoline invokes the main body of the interrupt handler. This patch adds a missing clobber list to the inline assembly block. It simply lists the caller-saved registers defined by the cdecl calling convention: EAX, ECX, and EDX. This is necessary, because the inline assembly block invokes idt_set_intr_gate_desc using a call instruction at the time the function containing the SET_INTERRUPT_HANDLER instance is executed. The idt_set_intr_gate_desc function is free to clobber EAX, ECX, and EDX according to cdecl. A Clang-generated implementation of idt_set_intr_gate_desc did in fact clobber those registers, resulting in incorrect operation of the code following an instance of SET_INTERRUPT_HANDLER. The change in this patch informs the compiler that those registers may be clobbered so that it can adjust the code that it outputs around the inline assembly block accordingly.
112 lines
4.9 KiB
C
112 lines
4.9 KiB
C
/*
|
|
* Copyright (C) 2015, Intel Corporation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef INTERRUPT_H
|
|
#define INTERRUPT_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "idt.h"
|
|
|
|
struct interrupt_context {
|
|
uint32_t edi;
|
|
uint32_t esi;
|
|
uint32_t ebp;
|
|
uint32_t esp;
|
|
uint32_t ebx;
|
|
uint32_t edx;
|
|
uint32_t ecx;
|
|
uint32_t eax;
|
|
uint32_t error_code;
|
|
uint32_t eip;
|
|
};
|
|
|
|
/* Helper macro to register interrupt handler function.
|
|
*
|
|
* num: Interrupt number (0-255)
|
|
* has_error_code: 0 if interrupt doesn't push error code onto the
|
|
* stack. Otherwise, set this argument to 1.
|
|
* handler: Pointer to function that should be called once the
|
|
* interrupt is raised. In case has_error_code == 0
|
|
* the function prototype should be the following:
|
|
* void handler(void)
|
|
* Otherwise, it should be:
|
|
* void handler(struct interrupt_context context)
|
|
*
|
|
* Since there is no easy way to write an Interrupt Service Routines
|
|
* (ISR) in C (for further information on this, see [1]), we provide
|
|
* this helper macro. It basically provides an assembly trampoline
|
|
* to a C function (handler parameter) which, indeed, handles the
|
|
* interrupt.
|
|
*
|
|
* [1] http://wiki.osdev.org/Interrupt_Service_Routines
|
|
*
|
|
* XXX: If you are debugging at assembly level, make sure you don't be misled
|
|
* by the "trampolineXX" symbol name. The suffix number is NOT related to the
|
|
* interrupt number at all. The suffix number is a unique random number to
|
|
* guarantee there is no symbol name clashing.
|
|
*/
|
|
#define SET_INTERRUPT_HANDLER(num, has_error_code, handler) \
|
|
do { \
|
|
__asm__ __volatile__ ( \
|
|
"push $trampoline%=\n\t" \
|
|
"push %0\n\t" \
|
|
"call %P1\n\t" \
|
|
"add $8, %%esp\n\t" \
|
|
"jmp skip_trampoline%=\n\t" \
|
|
".align 4\n\t" \
|
|
"trampoline%=:\n\t" \
|
|
" pushal\n\t" \
|
|
" call %P2\n\t" \
|
|
" popal\n\t" \
|
|
" .if " #has_error_code "\n\t" \
|
|
" add $4, %%esp\n\t" \
|
|
" .endif\n\t" \
|
|
" iret\n\t" \
|
|
"skip_trampoline%=:\n\t" \
|
|
:: "g" (num), "i" (idt_set_intr_gate_desc), "i" (handler) \
|
|
: "eax", "ecx", "edx" \
|
|
); \
|
|
} while (0)
|
|
|
|
/* Disable maskable hardware interrupts */
|
|
#define DISABLE_IRQ() \
|
|
do { \
|
|
__asm__ ("cli"); \
|
|
} while (0)
|
|
|
|
/* Enable maskable hardware interrupts */
|
|
#define ENABLE_IRQ() \
|
|
do { \
|
|
__asm__ ("sti"); \
|
|
} while (0)
|
|
|
|
#endif /* INTERRUPT_H */
|