Contiki 3.x
interrupt.h
1 /*
2  * Copyright (C) 2015, Intel Corporation. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  *
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifndef INTERRUPT_H
32 #define INTERRUPT_H
33 
34 #include <stdint.h>
35 #include "gdt-layout.h"
36 
37 #include "idt.h"
38 
39 struct interrupt_context {
40  /* The general-purpose register values are saved by the pushal instruction in
41  * the interrupt dispatcher. Having access to these saved values may be
42  * useful in some future interrupt or exception handler, and saving and later
43  * restoring them also enables the ISR to freely overwrite the EAX, ECX, and
44  * EDX registers as is permitted by the cdecl calling convention.
45  */
46  uint32_t edi;
47  uint32_t esi;
48  uint32_t ebp;
49  uint32_t esp;
50  uint32_t ebx;
51  uint32_t edx;
52  uint32_t ecx;
53  uint32_t eax;
54  /* These two values are pushed on the stack by the CPU when it delivers an
55  * exception with an associated error code. Currently, only the double fault
56  * handler accepts this structure as a parameter, and that type of exception
57  * does have an associated error code.
58  */
59  uint32_t error_code;
60  uint32_t eip;
61  /* The CPU pushes additional values beyond these on the stack, specifically
62  * the code segment descriptor and flags. If a privilege-level change occurs
63  * during delivery, the CPU additionally pushes the stack pointer and stack
64  * segment descriptor.
65  */
66 };
67 
68 #define ISR_STUB(label_str, has_error_code, handler_str, exc) \
69  "jmp 2f\n\t" \
70  ".align 4\n\t" \
71  label_str ":\n\t" \
72  " pushal\n\t" \
73  PROT_DOMAINS_ENTER_ISR(exc) \
74  " call " handler_str "\n\t" \
75  PROT_DOMAINS_LEAVE_ISR(exc) \
76  " popal\n\t" \
77  " .if " #has_error_code "\n\t" \
78  " add $4, %%esp\n\t" \
79  " .endif\n\t" \
80  " iret\n\t" \
81  "2:\n\t"
82 
83 /* Helper macro to register interrupt handler function.
84  *
85  * num: Interrupt number (0-255)
86  * has_error_code: 0 if interrupt doesn't push error code onto the
87  * stack. Otherwise, set this argument to 1.
88  * handler: Pointer to function that should be called once the
89  * interrupt is raised. In case has_error_code == 0
90  * the function prototype should be the following:
91  * void handler(void)
92  * Otherwise, it should be:
93  * void handler(struct interrupt_context context)
94  * exc: 0 if this is an interrupt, which should be handled
95  * at the interrupt privilege level. 1 if this is an
96  * exception, which should be handled at the
97  * exception privilege level.
98  * dpl: Privilege level for IDT descriptor, which is the
99  * numerically-highest privilege level that can
100  * generate this interrupt with a software interrupt
101  * instruction.
102  *
103  * Since there is no easy way to write an Interrupt Service Routines
104  * (ISR) in C (for further information on this, see [1]), we provide
105  * this helper macro. It basically provides an assembly trampoline
106  * to a C function (handler parameter) which, indeed, handles the
107  * interrupt.
108  *
109  * [1] http://wiki.osdev.org/Interrupt_Service_Routines
110  */
111 #define SET_INT_EXC_HANDLER(num, has_error_code, handler, exc, dpl) \
112  do { \
113  __asm__ __volatile__ ( \
114  "pushl %[_dpl_]\n\t" \
115  "pushl %[_cs_]\n\t" \
116  "pushl $1f\n\t" \
117  "pushl %[_isr_num_]\n\t" \
118  "call idt_set_intr_gate_desc\n\t" \
119  "add $16, %%esp\n\t" \
120  ISR_STUB("1", has_error_code, "%P[_handler_]", exc) \
121  : \
122  : [_isr_num_] "g" (num), \
123  [_handler_] "i" (handler), \
124  [_cs_] "i" (exc ? GDT_SEL_CODE_EXC : GDT_SEL_CODE_INT), \
125  [_dpl_] "i" (dpl) \
126  /* the invocation of idt_set_intr_gate_desc may clobber */ \
127  /* the caller-saved registers: */ \
128  : "eax", "ecx", "edx" \
129  ); \
130  } while (0)
131 #define SET_INTERRUPT_HANDLER(num, has_error_code, handler) \
132  SET_INT_EXC_HANDLER(num, has_error_code, handler, 0, PRIV_LVL_INT)
133 #define SET_EXCEPTION_HANDLER(num, has_error_code, handler) \
134  SET_INT_EXC_HANDLER(num, has_error_code, handler, 1, PRIV_LVL_EXC)
135 
136 /* Disable maskable hardware interrupts */
137 #define DISABLE_IRQ() \
138  do { \
139  __asm__ ("cli"); \
140  } while (0)
141 
142 /* Enable maskable hardware interrupts */
143 #define ENABLE_IRQ() \
144  do { \
145  __asm__ ("sti"); \
146  } while (0)
147 
148 #endif /* INTERRUPT_H */