]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/idt.cpp
update
[l4.git] / kernel / fiasco / src / kern / ia32 / idt.cpp
1 /*
2  * Fiasco Interrupt Descriptor Table (IDT) Code
3  */
4
5 INTERFACE:
6
7 #include "initcalls.h"
8 #include "kmem.h"
9 #include "mem_layout.h"
10 #include "types.h"
11 #include "x86desc.h"
12
13 class Idt_init_entry;
14
15 class Idt
16 {
17   friend class Jdb_kern_info_bench;
18 public:
19   // idt entries for 0x20 CPU exceptions, 0x10 IRQs, 7 syscalls,
20   // 0x3e/0x3f for APIC exceptions
21   static const unsigned _idt_max = 0xa0;
22 private:
23   static const Address  _idt = Mem_layout::Idt;
24 };
25
26 IMPLEMENTATION:
27
28 #include <cassert>
29 #include "gdt.h"
30 #include "idt_init.h"
31 #include "irq_chip.h"
32 #include "mem_unit.h"
33 #include "paging.h"
34 #include "panic.h"
35 #include "vmem_alloc.h"
36
37 /**
38  * IDT write-protect/write-unprotect function.
39  * @param writable true if IDT should be made writable, false otherwise
40  */
41 PRIVATE static
42 void
43 Idt::set_writable(bool writable)
44 {
45   Pdir::Iter e = Kmem::dir()->walk(Virt_addr(_idt));
46
47   // Make sure page directory entry is valid and not a 4MB page
48   assert (e.e->valid() && e.shift() < Config::SUPERPAGE_SHIFT);
49
50   if (writable)
51     e.e->add_attr(Pt_entry::Writable); // Make read-write
52   else
53     e.e->del_attr(Pt_entry::Writable); // Make read-only
54
55   Mem_unit::tlb_flush (_idt);
56 }
57
58 PUBLIC static FIASCO_INIT
59 void
60 Idt::init_table(Idt_init_entry *src)
61 {
62   Idt_entry *entries = (Idt_entry*)_idt;
63
64   while (src->entry)
65     {
66       entries[src->vector] = 
67         ((src->type & 0x1f) == 0x05) // task gate?
68           ? Idt_entry(src->entry, src->type)
69           : Idt_entry(src->entry, Gdt::gdt_code_kernel, src->type);
70       src++;
71     }
72 }
73
74 /**
75  * IDT initialization function. Sets up initial interrupt vectors.
76  * It also write-protects the IDT because of the infamous Pentium F00F bug.
77  */
78 PUBLIC static FIASCO_INIT
79 void
80 Idt::init()
81 {
82   if (!Vmem_alloc::page_alloc((void *) _idt, Vmem_alloc::ZERO_FILL))
83     panic("IDT allocation failure");
84
85   init_table((Idt_init_entry*)&idt_init_table);
86   load();
87
88   set_writable(false);
89 }
90
91
92 PUBLIC static
93 void
94 Idt::load()
95 {
96   Pseudo_descriptor desc(_idt, _idt_max*sizeof(Idt_entry)-1);
97   set(&desc);
98 }
99
100 /**
101  * IDT patching function.
102  * Allows to change interrupt gate vectors at runtime.
103  * It makes the IDT writable for the duration of this operation.
104  * @param vector interrupt vector to be modified
105  * @param func new handler function for this interrupt vector
106  * @param user true if user mode can use this vector, false otherwise
107  */
108 PUBLIC static
109 void
110 Idt::set_entry(unsigned vector, Address entry, bool user)
111 {
112   assert (vector < _idt_max);
113
114   set_writable(true);
115
116   Idt_entry *entries = (Idt_entry*)_idt;
117   if (entry)
118     entries[vector] = Idt_entry(entry, Gdt::gdt_code_kernel,
119                                 Idt_entry::Access_intr_gate |
120                                 (user ? Idt_entry::Access_user 
121                                       : Idt_entry::Access_kernel));
122   else
123     entries[vector].clear();
124
125   set_writable(false);
126 }
127
128 PUBLIC static
129 Address
130 Idt::get_entry(unsigned vector)
131 {
132   assert (vector < _idt_max);
133   Idt_entry *entries = (Idt_entry*)_idt;
134   return entries[vector].offset();
135 }
136
137 PUBLIC static inline
138 Address
139 Idt::idt()
140 {
141   return _idt;
142 }
143
144
145 //---------------------------------------------------------------------------
146 IMPLEMENTATION[ia32 | amd64]:
147
148 #include "config.h"
149 #include "timer.h"
150
151 /**
152  * IDT loading function.
153  * Loads IDT base and limit into the CPU.
154   * @param desc IDT descriptor (base address, limit)
155   */  
156 PUBLIC static inline
157 void
158 Idt::set(Pseudo_descriptor *desc)
159 {
160   asm volatile ("lidt %0" : : "m" (*desc));
161 }
162
163 PUBLIC static inline
164 void
165 Idt::get(Pseudo_descriptor *desc)
166 {
167   asm volatile ("sidt %0" : "=m" (*desc) : : "memory");
168 }
169
170 extern "C" void entry_int_timer();
171 extern "C" void entry_int_timer_slow();
172 extern "C" void entry_int_timer_stop();
173 extern "C" void entry_int7();
174 extern "C" void entry_intf();
175 extern "C" void entry_int_pic_ignore();
176
177 /**
178  * Set IDT vector to the normal timer interrupt handler.
179  */
180 PUBLIC static
181 void
182 Idt::set_vectors_run()
183 {
184   Address func = (Config::esc_hack || Config::watchdog ||
185                   Config::serial_esc==Config::SERIAL_ESC_NOIRQ)
186                     ? (Address)entry_int_timer_slow // slower for debugging
187                     : (Address)entry_int_timer;     // non-debugging
188
189   set_entry(Config::scheduler_irq_vector, func, false);
190   if (!Irq_chip::hw_chip->is_free(0x7))
191     Irq_chip::hw_chip->reset(0x07);
192
193   if (!Irq_chip::hw_chip->is_free(0xf))
194     Irq_chip::hw_chip->reset(0x0f);
195 }
196
197 /**
198  * Set IDT vector to a dummy vector if Config::getchar_does_hlt is true.
199  */
200 PUBLIC static
201 void
202 Idt::set_vectors_stop()
203 {
204   // acknowledge timer interrupt once to keep timer interrupt alive because
205   // we could be called from thread_timer_interrupt_slow() before ack
206   Timer::acknowledge();
207
208   // set timer interrupt to dummy doing nothing
209   set_entry(Config::scheduler_irq_vector, (Address)entry_int_timer_stop, false);
210
211   // From ``8259A PROGRAMMABLE INTERRUPT CONTROLLER (8259A 8259A-2)'': If no
212   // interrupt request is present at step 4 of either sequence (i. e. the
213   // request was too short in duration) the 8259A will issue an interrupt
214   // level 7. Both the vectoring bytes and the CAS lines will look like an
215   // interrupt level 7 was requested.
216   set_entry(0x27, (Address)entry_int_pic_ignore, false);
217   set_entry(0x2f, (Address)entry_int_pic_ignore, false);
218 }
219
220
221 //---------------------------------------------------------------------------
222 IMPLEMENTATION[ux]:
223
224 #include "emulation.h"
225
226 PUBLIC static
227 void
228 Idt::set(Pseudo_descriptor *desc)
229 {
230   Emulation::lidt(desc);
231 }
232
233 PUBLIC static
234 void
235 Idt::get(Pseudo_descriptor *desc)
236 {
237   Emulation::sidt(desc);
238 }
239
240 PUBLIC static
241 void
242 Idt::set_vectors_run()
243 {
244   extern char entry_int_timer[];
245   set_entry(Config::scheduler_irq_vector, (Address)entry_int_timer, false);
246 }