]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/idt.cpp
9052e08f057efd1f3d2c0b036bf4dcaa0315a80c
[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   set_vectors_run();
87   load();
88
89   set_writable (false);
90 }
91
92
93 PUBLIC static
94 void
95 Idt::load()
96 {
97   Pseudo_descriptor desc(_idt, _idt_max*sizeof(Idt_entry)-1);
98   set (&desc);
99 }
100
101 /**
102  * IDT patching function.
103  * Allows to change interrupt gate vectors at runtime.
104  * It makes the IDT writable for the duration of this operation.
105  * @param vector interrupt vector to be modified
106  * @param func new handler function for this interrupt vector
107  * @param user true if user mode can use this vector, false otherwise
108  */
109 PUBLIC static
110 void
111 Idt::set_entry(unsigned vector, Address entry, bool user)
112 {
113   assert (vector < _idt_max);
114
115   set_writable (true);
116
117   Idt_entry *entries = (Idt_entry*)_idt;
118   if (entry)
119     entries[vector] = Idt_entry(entry, Gdt::gdt_code_kernel,
120                                 Idt_entry::Access_intr_gate |
121                                 (user ? Idt_entry::Access_user 
122                                       : Idt_entry::Access_kernel));
123   else
124     entries[vector].clear();
125
126   set_writable (false);
127 }
128
129 PUBLIC static
130 Address
131 Idt::get_entry(unsigned vector)
132 {
133   assert (vector < _idt_max);
134   Idt_entry *entries = (Idt_entry*)_idt;
135   return entries[vector].offset();
136 }
137
138 PUBLIC static inline
139 Address
140 Idt::idt()
141 {
142   return _idt;
143 }
144
145
146 //---------------------------------------------------------------------------
147 IMPLEMENTATION[ia32 | amd64]:
148
149 #include "config.h"
150 #include "timer.h"
151
152 /**
153  * IDT loading function.
154  * Loads IDT base and limit into the CPU.
155   * @param desc IDT descriptor (base address, limit)
156   */  
157 PUBLIC static inline
158 void
159 Idt::set (Pseudo_descriptor *desc)
160 {
161   asm volatile ("lidt %0" : : "m" (*desc));
162 }
163
164 PUBLIC static inline
165 void
166 Idt::get (Pseudo_descriptor *desc)
167 {
168   asm volatile ("sidt %0" : "=m" (*desc) : : "memory");
169 }
170
171 extern "C" void entry_int_timer();
172 extern "C" void entry_int_timer_slow();
173 extern "C" void entry_int_timer_stop();
174 extern "C" void entry_int7();
175 extern "C" void entry_intf();
176 extern "C" void entry_int_pic_ignore();
177
178 /**
179  * Set IDT vector to the normal timer interrupt handler.
180  */
181 PUBLIC static
182 void
183 Idt::set_vectors_run()
184 {
185   Address func = (Config::esc_hack || Config::watchdog ||
186                   Config::serial_esc==Config::SERIAL_ESC_NOIRQ)
187                     ? (Address)entry_int_timer_slow // slower for debugging
188                     : (Address)entry_int_timer;     // non-debugging
189
190   set_entry (Config::scheduler_irq_vector, func, false);
191   if (!Irq_chip::hw_chip->is_free(0x7))
192     Irq_chip::hw_chip->reset(0x07);
193
194   if (!Irq_chip::hw_chip->is_free(0xf))
195     Irq_chip::hw_chip->reset(0x0f);
196 }
197
198 /**
199  * Set IDT vector to a dummy vector if Config::getchar_does_hlt is true.
200  */
201 PUBLIC static
202 void
203 Idt::set_vectors_stop()
204 {
205   // acknowledge timer interrupt once to keep timer interrupt alive because
206   // we could be called from thread_timer_interrupt_slow() before ack
207   Timer::acknowledge();
208
209   // set timer interrupt to dummy doing nothing
210   set_entry(Config::scheduler_irq_vector, 
211             (Address) entry_int_timer_stop, false);
212
213   // From ``8259A PROGRAMMABLE INTERRUPT CONTROLLER (8259A 8259A-2)'': If no
214   // interrupt request is present at step 4 of either sequence (i. e. the
215   // request was too short in duration) the 8259A will issue an interrupt
216   // level 7. Both the vectoring bytes and the CAS lines will look like an
217   // interrupt level 7 was requested.
218   set_entry(0x27, (Address) entry_int_pic_ignore, false);
219   set_entry(0x2f, (Address) entry_int_pic_ignore, false);
220 }
221
222
223 //---------------------------------------------------------------------------
224 IMPLEMENTATION[ux]:
225
226 #include "emulation.h"
227
228 PUBLIC static
229 void
230 Idt::set (Pseudo_descriptor *desc)
231 {
232   Emulation::lidt (desc);
233 }
234
235 PUBLIC static
236 void
237 Idt::get (Pseudo_descriptor *desc)
238 {
239   Emulation::sidt (desc);
240 }
241
242 PUBLIC static
243 void
244 Idt::set_vectors_run()
245 {
246   extern char entry_int_timer[];
247   set_entry (Config::scheduler_irq_vector, (Address)entry_int_timer, false);
248 }