nes-proj/cpu/x86/init/common/interrupt.h

149 lines
7.1 KiB
C
Raw Normal View History

x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
/*
* 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 "gdt-layout.h"
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
#include "idt.h"
struct interrupt_context {
/* The general-purpose register values are saved by the pushal instruction in
* the interrupt dispatcher. Having access to these saved values may be
* useful in some future interrupt or exception handler, and saving and later
* restoring them also enables the ISR to freely overwrite the EAX, ECX, and
* EDX registers as is permitted by the cdecl calling convention.
*/
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp;
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
/* These two values are pushed on the stack by the CPU when it delivers an
* exception with an associated error code. Currently, only the double fault
* handler accepts this structure as a parameter, and that type of exception
* does have an associated error code.
*/
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
uint32_t error_code;
uint32_t eip;
/* The CPU pushes additional values beyond these on the stack, specifically
* the code segment descriptor and flags. If a privilege-level change occurs
* during delivery, the CPU additionally pushes the stack pointer and stack
* segment descriptor.
*/
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
};
#define ISR_STUB(label_str, has_error_code, handler_str, exc) \
"jmp 2f\n\t" \
".align 4\n\t" \
label_str ":\n\t" \
" pushal\n\t" \
PROT_DOMAINS_ENTER_ISR(exc) \
" call " handler_str "\n\t" \
PROT_DOMAINS_LEAVE_ISR(exc) \
" popal\n\t" \
" .if " #has_error_code "\n\t" \
" add $4, %%esp\n\t" \
" .endif\n\t" \
" iret\n\t" \
"2:\n\t"
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
/* 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)
* exc: 0 if this is an interrupt, which should be handled
* at the interrupt privilege level. 1 if this is an
* exception, which should be handled at the
* exception privilege level.
* dpl: Privilege level for IDT descriptor, which is the
* numerically-highest privilege level that can
* generate this interrupt with a software interrupt
* instruction.
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
*
* 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
*/
#define SET_INT_EXC_HANDLER(num, has_error_code, handler, exc, dpl) \
do { \
__asm__ __volatile__ ( \
"pushl %[_dpl_]\n\t" \
"pushl %[_cs_]\n\t" \
"pushl $1f\n\t" \
"pushl %[_isr_num_]\n\t" \
"call idt_set_intr_gate_desc\n\t" \
"add $16, %%esp\n\t" \
ISR_STUB("1", has_error_code, "%P[_handler_]", exc) \
: \
: [_isr_num_] "g" (num), \
[_handler_] "i" (handler), \
[_cs_] "i" (exc ? GDT_SEL_CODE_EXC : GDT_SEL_CODE_INT), \
[_dpl_] "i" (dpl) \
/* the invocation of idt_set_intr_gate_desc may clobber */ \
/* the caller-saved registers: */ \
: "eax", "ecx", "edx" \
); \
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
} while (0)
#define SET_INTERRUPT_HANDLER(num, has_error_code, handler) \
SET_INT_EXC_HANDLER(num, has_error_code, handler, 0, PRIV_LVL_INT)
#define SET_EXCEPTION_HANDLER(num, has_error_code, handler) \
SET_INT_EXC_HANDLER(num, has_error_code, handler, 1, PRIV_LVL_EXC)
x86: Introduce interrupt.h This patch introduces the interrupt.h header file which provides some helper macros to set a interrupt handler and disable/enable maskable hardware interrupts. Since there is no easy way to write an Interrupt Service Routines (ISR) in C (for further information on this, see [1]), we introduce the SET_INTERRUPT_HANDLER helper macro. The macro does two things: 1) Defines an assembly trampolin to a C function that will, indeed, handle the interrupt. 2) Sets the corresponding interrupt gate descriptor in IDT. The macro usage is pretty straightforward. The macro is defined as SET_INTERRUPT_HANDLER(num, has_error_code, handler) where: @num: Interrupt number (0-255) @has_error_code: 0 if processor 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) For instance, let's say we want to set a handler for a device interrupt (for example, interrupt number 101). Remember, hardware interrupts don't have error code. So we should have something like this: void interrupt_handler(void) { /* Handling code here */ } void my_device_init(void) { ... SET_INTERRUPT_HANDLER(101, 0, interrupt_handler); ... } Now, let's say we want to set an interrupt handler for Page Fault (interrupt number 14). Some exceptions, such as Page Fault, pushes an error code onto the stack and may require registers values in order to be properly be handled. Thus, the code should look like this: void pagefault_handler(struct interrupt_context context) { /* Handling code here */ } void init_memory(void) { ... SET_INTERRUPT_HANDLER(14, 1, pagefault_handler); ... } For further information about exceptions and error code, refer to Intel Combined Manual, Vol. 3, Sections 6.3 and 6.13. Finally, we don't define any API to unregister interrupt handlers since we believe that it wouldn't be useful at all, at least at this moment. Considering Contiki's context, interrupt handler registration is pretty "static" and defined at compile-time by platform code (or the device drivers used by the platform). [1] http://wiki.osdev.org/Interrupt_Service_Routines
2015-04-15 18:05:12 +00:00
/* 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 */