]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/apic-ia32.cpp
Update
[l4.git] / kernel / fiasco / src / kern / ia32 / apic-ia32.cpp
1 INTERFACE:
2
3 #include "per_cpu_data.h"
4 #include "types.h"
5 #include "initcalls.h"
6 #include "pm.h"
7
8 class Return_frame;
9 class Cpu;
10
11 class Apic : public Pm_object
12 {
13 public:
14   static void init(bool resume = false) FIASCO_INIT_AND_PM;
15   Unsigned32 apic_id() const { return _id; }
16   Unsigned32 cpu_id() const { return _id >> 24; }
17
18   static Per_cpu<Static_object<Apic> > apic;
19
20 private:
21   Apic(const Apic&) = delete;
22   Apic &operator = (Apic const &) = delete;
23
24   Unsigned32 _id;
25   Unsigned32 _saved_apic_timer;
26
27   static void                   error_interrupt(Return_frame *regs)
28                                 asm ("apic_error_interrupt") FIASCO_FASTCALL;
29
30   static int                    present;
31   static int                    good_cpu;
32   static const                  Address io_base;
33   static Address                phys_base;
34   static unsigned               timer_divisor;
35   static unsigned               frequency_khz;
36   static Unsigned64             scaler_us_to_apic;
37
38   enum
39   {
40     APIC_id                     = 0x20,
41     APIC_lvr                    = 0x30,
42     APIC_tpr                    = 0x80,
43     APIC_tpri_mask              = 0xFF,
44     APIC_eoi                    = 0xB0,
45     APIC_ldr                    = 0xD0,
46     APIC_ldr_mask               = 0xFFul << 24,
47     APIC_dfr                    = 0xE0,
48     APIC_spiv                   = 0xF0,
49     APIC_isr                    = 0x100,
50     APIC_tmr                    = 0x180,
51     APIC_irr                    = 0x200,
52     APIC_esr                    = 0x280,
53     APIC_lvtt                   = 0x320,
54     APIC_lvtthmr                = 0x330,
55     APIC_lvtpc                  = 0x340,
56     APIC_lvt0                   = 0x350,
57     APIC_timer_base_div         = 0x2,
58     APIC_lvt1                   = 0x360,
59     APIC_lvterr                 = 0x370,
60     APIC_tmict                  = 0x380,
61     APIC_tmcct                  = 0x390,
62     APIC_tdcr                   = 0x3E0,
63
64     APIC_snd_pending            = 1 << 12,
65     APIC_input_polarity         = 1 << 13,
66     APIC_lvt_remote_irr         = 1 << 14,
67     APIC_lvt_level_trigger      = 1 << 15,
68     APIC_lvt_masked             = 1 << 16,
69     APIC_lvt_timer_periodic     = 1 << 17,
70     APIC_tdr_div_1              = 0xB,
71     APIC_tdr_div_2              = 0x0,
72     APIC_tdr_div_4              = 0x1,
73     APIC_tdr_div_8              = 0x2,
74     APIC_tdr_div_16             = 0x3,
75     APIC_tdr_div_32             = 0x8,
76     APIC_tdr_div_64             = 0x9,
77     APIC_tdr_div_128            = 0xA,
78   };
79
80   enum
81   {
82     Mask                        =  1,
83     Trigger_mode                =  2,
84     Remote_irr                  =  4,
85     Pin_polarity                =  8,
86     Delivery_state              = 16,
87     Delivery_mode               = 32,
88   };
89
90   enum
91   {
92     APIC_base_msr               = 0x1b,
93   };
94 };
95
96 extern unsigned apic_spurious_interrupt_bug_cnt;
97 extern unsigned apic_spurious_interrupt_cnt;
98 extern unsigned apic_error_cnt;
99
100
101 //----------------------------------------------------------------------------
102 IMPLEMENTATION:
103 #include "cpu.h"
104
105 DEFINE_PER_CPU Per_cpu<Static_object<Apic> >  Apic::apic;
106
107 PUBLIC inline
108 Apic::Apic(Cpu_number cpu) : _id(get_id()) { register_pm(cpu); }
109
110
111 PRIVATE static
112 Cpu &
113 Apic::cpu() { return *Cpu::boot_cpu(); }
114
115 // FIXME: workaround for missing lambdas in gcc < 4.5
116 namespace {
117 struct By_id
118 {
119   Unsigned32 p;
120   By_id(Unsigned32 p) : p(p) {}
121   bool operator () (Apic const *a) const { return a && a->apic_id() == p; }
122 };
123 }
124
125 PUBLIC static
126 Cpu_number
127 Apic::find_cpu(Unsigned32 phys_id)
128 {
129   return apic.find_cpu(By_id(phys_id));
130 }
131
132 PUBLIC void
133 Apic::pm_on_suspend(Cpu_number)
134 {
135   _saved_apic_timer = timer_reg_read();
136 }
137
138 //----------------------------------------------------------------------------
139 IMPLEMENTATION [!mp]:
140
141 PUBLIC void
142 Apic::pm_on_resume(Cpu_number)
143 {
144   Apic::init(true);
145   timer_reg_write(_saved_apic_timer);
146 }
147
148 //----------------------------------------------------------------------------
149 IMPLEMENTATION [mp]:
150
151 PUBLIC void
152 Apic::pm_on_resume(Cpu_number cpu)
153 {
154   if (cpu == Cpu_number::boot_cpu())
155     Apic::init(true);
156   else
157     Apic::init_ap();
158   timer_reg_write(_saved_apic_timer);
159 }
160
161
162 //----------------------------------------------------------------------------
163 IMPLEMENTATION[ia32]:
164
165 PUBLIC static inline
166 Unsigned32
167 Apic::us_to_apic(Unsigned64 us)
168 {
169   Unsigned32 apic, dummy1, dummy2;
170   asm ("movl  %%edx, %%ecx              \n\t"
171        "mull  %4                        \n\t"
172        "movl  %%ecx, %%eax              \n\t"
173        "movl  %%edx, %%ecx              \n\t"
174        "mull  %4                        \n\t"
175        "addl  %%ecx, %%eax              \n\t"
176        "shll  $11, %%eax                \n\t"
177       :"=a" (apic), "=d" (dummy1), "=&c" (dummy2)
178       : "A" (us),   "g" (scaler_us_to_apic)
179        );
180   return apic;
181 }
182
183 IMPLEMENTATION[amd64]:
184
185 PUBLIC static inline
186 Unsigned32
187 Apic::us_to_apic(Unsigned64 us)
188 {
189   Unsigned32 apic, dummy;
190   asm ("mulq  %3                        \n\t"
191        "shrq  $21,%%rax                 \n\t"
192       :"=a"(apic), "=d"(dummy)
193       :"a"(us), "g"(scaler_us_to_apic)
194       );
195   return apic;
196 }
197
198 IMPLEMENTATION[ia32,amd64]:
199
200 #include <cassert>
201 #include <cstdio>
202 #include <cstdlib>
203 #include <cstring>
204
205 #include "config.h"
206 #include "cpu.h"
207 #include "cpu_lock.h"
208 #include "entry_frame.h"
209 #include "globals.h"
210 #include "io.h"
211 #include "kmem.h"
212 #include "kip.h"
213 #include "lock_guard.h"
214 #include "panic.h"
215 #include "processor.h"
216 #include "regdefs.h"
217 #include "pic.h"
218 #include "pit.h"
219
220 unsigned apic_spurious_interrupt_bug_cnt;
221 unsigned apic_spurious_interrupt_cnt;
222 unsigned apic_error_cnt;
223 Address  apic_io_base;
224
225 int        Apic::present;
226 int        Apic::good_cpu;
227 const Address Apic::io_base = Mem_layout::Local_apic_page;
228 Address    Apic::phys_base;
229 unsigned   Apic::timer_divisor = 1;
230 unsigned   Apic::frequency_khz;
231 Unsigned64 Apic::scaler_us_to_apic;
232
233 int ignore_invalid_apic_reg_access;
234
235 PUBLIC static inline
236 Unsigned32
237 Apic::get_id()
238 {
239   return reg_read(APIC_id) & 0xff000000;
240 }
241
242 PRIVATE static inline
243 Unsigned32
244 Apic::get_version()
245 {
246   return reg_read(APIC_lvr) & 0xFF;
247 }
248
249 PRIVATE static inline NOEXPORT
250 int
251 Apic::is_integrated()
252 {
253   return reg_read(APIC_lvr) & 0xF0;
254 }
255
256 PRIVATE static inline NOEXPORT
257 Unsigned32
258 Apic::get_max_lvt_local()
259 {
260   return ((reg_read(APIC_lvr) >> 16) & 0xFF);
261 }
262
263 PRIVATE static inline NOEXPORT
264 Unsigned32
265 Apic::get_num_errors()
266 {
267   reg_write(APIC_esr, 0);
268   return reg_read(APIC_esr);
269 }
270
271 PRIVATE static inline NOEXPORT
272 void
273 Apic::clear_num_errors()
274 {
275   reg_write(APIC_esr, 0);
276   reg_write(APIC_esr, 0);
277 }
278
279 PUBLIC static inline
280 unsigned
281 Apic::get_frequency_khz()
282 {
283   return frequency_khz;
284 }
285
286 PUBLIC static inline
287 Unsigned32
288 Apic::reg_read(unsigned reg)
289 {
290   return *((volatile Unsigned32*)(io_base + reg));
291 }
292
293 PUBLIC static inline
294 void
295 Apic::reg_write(unsigned reg, Unsigned32 val)
296 {
297   *((volatile Unsigned32*)(io_base + reg)) = val;
298 }
299
300 PUBLIC static inline
301 int
302 Apic::reg_delivery_mode(Unsigned32 val)
303 {
304   return (val >> 8) & 7;
305 }
306
307 PUBLIC static inline
308 int
309 Apic::reg_lvt_vector(Unsigned32 val)
310 {
311   return val & 0xff;
312 }
313
314 PUBLIC static inline
315 Unsigned32
316 Apic::timer_reg_read()
317 {
318   return reg_read(APIC_tmcct);
319 }
320
321 PUBLIC static inline
322 Unsigned32
323 Apic::timer_reg_read_initial()
324 {
325   return reg_read(APIC_tmict);
326 }
327
328 PUBLIC static inline
329 void
330 Apic::timer_reg_write(Unsigned32 val)
331 {
332   reg_read(APIC_tmict);
333   reg_write(APIC_tmict, val);
334 }
335
336 PUBLIC static inline NEEDS["cpu.h"]
337 Address
338 Apic::apic_page_phys()
339 { return Cpu::rdmsr(APIC_base_msr) & 0xfffff000; }
340
341 // set the global pagetable entry for the Local APIC device registers
342 PUBLIC
343 static FIASCO_INIT_AND_PM
344 void
345 Apic::map_apic_page()
346 {
347   Address offs;
348   Address base = apic_page_phys();
349   // We should not change the physical address of the Local APIC page if
350   // possible since some versions of VMware would complain about a
351   // non-implemented feature
352   Kmem::map_phys_page(base, Mem_layout::Local_apic_page,
353                       false, true, &offs);
354
355   Kip::k()->add_mem_region(Mem_desc(base, base + Config::PAGE_SIZE - 1, Mem_desc::Reserved));
356
357   assert(offs == 0);
358 }
359
360 // check CPU type if APIC could be present
361 static FIASCO_INIT_AND_PM
362 int
363 Apic::test_cpu()
364 {
365   if (!cpu().can_wrmsr() || !(cpu().features() & FEAT_TSC))
366     return 0;
367
368   if (cpu().vendor() == Cpu::Vendor_intel)
369     {
370       if (cpu().family() == 15)
371         return 1;
372       if (cpu().family() >= 6)
373         return 1;
374     }
375   if (cpu().vendor() == Cpu::Vendor_amd && cpu().family() >= 6)
376     return 1;
377
378   return 0;
379 }
380
381 // test if APIC present
382 static inline
383 int
384 Apic::test_present()
385 {
386   return cpu().features() & FEAT_APIC;
387 }
388
389 PUBLIC static inline
390 void
391 Apic::timer_enable_irq()
392 {
393   Unsigned32 tmp_val;
394
395   tmp_val = reg_read(APIC_lvtt);
396   tmp_val &= ~(APIC_lvt_masked);
397   reg_write(APIC_lvtt, tmp_val);
398 }
399
400 PUBLIC static inline
401 void
402 Apic::timer_disable_irq()
403 {
404   Unsigned32 tmp_val;
405
406   tmp_val = reg_read(APIC_lvtt);
407   tmp_val |= APIC_lvt_masked;
408   reg_write(APIC_lvtt, tmp_val);
409 }
410
411 PUBLIC static inline
412 int
413 Apic::timer_is_irq_enabled()
414 {
415   return ~reg_read(APIC_lvtt) & APIC_lvt_masked;
416 }
417
418 PUBLIC static inline
419 void
420 Apic::timer_set_periodic()
421 {
422   Unsigned32 tmp_val = reg_read(APIC_lvtt);
423   tmp_val |= APIC_lvt_timer_periodic;
424   reg_write(APIC_lvtt, tmp_val);
425 }
426
427 PUBLIC static inline
428 void
429 Apic::timer_set_one_shot()
430 {
431   Unsigned32 tmp_val = reg_read(APIC_lvtt);
432   tmp_val &= ~APIC_lvt_timer_periodic;
433   reg_write(APIC_lvtt, tmp_val);
434 }
435
436 PUBLIC static inline
437 void
438 Apic::timer_assign_irq_vector(unsigned vector)
439 {
440   Unsigned32 tmp_val = reg_read(APIC_lvtt);
441   tmp_val &= 0xffffff00;
442   tmp_val |= vector;
443   reg_write(APIC_lvtt, tmp_val);
444 }
445
446 PUBLIC static inline
447 void
448 Apic::irq_ack()
449 {
450   reg_read(APIC_spiv);
451   reg_write(APIC_eoi, 0);
452 }
453
454 static
455 void
456 Apic::timer_set_divisor(unsigned newdiv)
457 {
458   int i;
459   int div = -1;
460   int divval = newdiv;
461   Unsigned32 tmp_value;
462
463   static int divisor_tab[8] =
464     {
465       APIC_tdr_div_1,  APIC_tdr_div_2,  APIC_tdr_div_4,  APIC_tdr_div_8,
466       APIC_tdr_div_16, APIC_tdr_div_32, APIC_tdr_div_64, APIC_tdr_div_128
467     };
468
469   for (i=0; i<8; i++)
470     {
471       if (divval & 1)
472         {
473           if (divval & ~1)
474             {
475               printf("bad APIC divisor %u\n", newdiv);
476               return;
477             }
478           div = divisor_tab[i];
479           break;
480         }
481       divval >>= 1;
482     }
483
484   if (div != -1)
485     {
486       timer_divisor = newdiv;
487       tmp_value = reg_read(APIC_tdcr);
488       tmp_value &= ~0x1F;
489       tmp_value |= div;
490       reg_write(APIC_tdcr, tmp_value);
491     }
492 }
493
494 static
495 int
496 Apic::get_max_lvt()
497 {
498   return is_integrated() ? get_max_lvt_local() : 2;
499 }
500
501 PUBLIC static inline
502 int
503 Apic::have_pcint()
504 {
505   return (present && (get_max_lvt() >= 4));
506 }
507
508 PUBLIC static inline
509 int
510 Apic::have_tsint()
511 {
512   return (present && (get_max_lvt() >= 5));
513 }
514
515 // check if APIC is working (check timer functionality)
516 static FIASCO_INIT_AND_PM
517 int
518 Apic::check_working()
519 {
520   Unsigned64 tsc_until;
521
522   timer_disable_irq();
523   timer_set_divisor(1);
524   timer_reg_write(0x10000000);
525
526   tsc_until = Cpu::rdtsc() + 0x400;  // we only have to wait for one bus cycle
527
528   do
529     {
530       if (timer_reg_read() != 0x10000000)
531         return 1;
532     } while (Cpu::rdtsc() < tsc_until);
533
534   return 0;
535 }
536
537 static FIASCO_INIT_CPU_AND_PM
538 void
539 Apic::init_spiv()
540 {
541   Unsigned32 tmp_val;
542
543   tmp_val = reg_read(APIC_spiv);
544   tmp_val |= (1<<8);            // enable APIC
545   tmp_val &= ~(1<<9);           // enable Focus Processor Checking
546   tmp_val &= ~0xff;
547   tmp_val |= APIC_IRQ_BASE + 0xf; // Set spurious IRQ vector to 0x3f
548                               // bit 0..3 are hardwired to 1 on PPro!
549   reg_write(APIC_spiv, tmp_val);
550 }
551
552 PUBLIC static inline NEEDS[Apic::reg_write]
553 void
554 Apic::tpr(unsigned prio)
555 { reg_write(APIC_tpr, prio); }
556
557 PUBLIC static inline NEEDS[Apic::reg_read]
558 unsigned
559 Apic::tpr()
560 { return reg_read(APIC_tpr); }
561
562 static FIASCO_INIT_CPU_AND_PM
563 void
564 Apic::init_tpr()
565 {
566   reg_write(APIC_tpr, 0);
567 }
568
569 // activate APIC error interrupt
570 static FIASCO_INIT_CPU_AND_PM
571 void
572 Apic::enable_errors()
573 {
574   if (is_integrated())
575     {
576       Unsigned32 tmp_val, before, after;
577
578       if (get_max_lvt() > 3)
579         clear_num_errors();
580       before = get_num_errors();
581
582       tmp_val = reg_read(APIC_lvterr);
583       tmp_val &= 0xfffeff00;         // unmask error IRQ vector
584       tmp_val |= APIC_IRQ_BASE + 3;  // Set error IRQ vector to 0x63
585       reg_write(APIC_lvterr, tmp_val);
586
587       if (get_max_lvt() > 3)
588         clear_num_errors();
589       after = get_num_errors();
590       printf("APIC ESR value before/after enabling: %08x/%08x\n",
591             before, after);
592     }
593 }
594
595 // activate APIC after activating by MSR was successful
596 // see "Intel Architecture Software Developer's Manual,
597 //      Volume 3: System Programming Guide, Appendix E"
598 static FIASCO_INIT_AND_PM
599 void
600 Apic::route_pic_through_apic()
601 {
602   Unsigned32 tmp_val;
603   auto guard = lock_guard(cpu_lock);
604
605   // mask 8259 interrupts
606   Unsigned16 old_irqs = Pic::disable_all_save();
607
608   // set LINT0 to ExtINT, edge triggered
609   tmp_val = reg_read(APIC_lvt0);
610   tmp_val &= 0xfffe5800;
611   tmp_val |= 0x00000700;
612   reg_write(APIC_lvt0, tmp_val);
613
614   // set LINT1 to NMI, edge triggered
615   tmp_val = reg_read(APIC_lvt1);
616   tmp_val &= 0xfffe5800;
617   tmp_val |= 0x00000400;
618   reg_write(APIC_lvt1, tmp_val);
619
620   // unmask 8259 interrupts
621   Pic::restore_all(old_irqs);
622
623   printf("APIC was disabled --- routing PIC through APIC\n");
624 }
625
626 static FIASCO_INIT_CPU_AND_PM
627 void
628 Apic::init_lvt()
629 {
630   auto guard = lock_guard(cpu_lock);
631
632   // mask timer interrupt and set vector to _not_ invalid value
633   reg_write(APIC_lvtt, reg_read(APIC_lvtt) | APIC_lvt_masked | 0xff);
634
635   if (have_pcint())
636     // mask performance interrupt and set vector to a valid value
637     reg_write(APIC_lvtpc, reg_read(APIC_lvtpc) | APIC_lvt_masked | 0xff);
638
639   if (have_tsint())
640     // mask thermal sensor interrupt and set vector to a valid value
641     reg_write(APIC_lvtthmr, reg_read(APIC_lvtthmr) | APIC_lvt_masked | 0xff);
642 }
643
644 // give us a hint if we have an APIC but it is disabled
645 PUBLIC static
646 int
647 Apic::test_present_but_disabled()
648 {
649   if (!good_cpu)
650     return 0;
651
652   Unsigned64 msr = Cpu::rdmsr(APIC_base_msr);
653   return ((msr & 0xffffff000ULL) == 0xfee00000ULL);
654 }
655
656 // activate APIC by writing to appropriate MSR
657 static FIASCO_INIT_CPU_AND_PM
658 void
659 Apic::activate_by_msr()
660 {
661   Unsigned64 msr;
662
663   msr = Cpu::rdmsr(APIC_base_msr);
664   phys_base = msr & 0xfffff000;
665   msr |= (1<<11);
666   Cpu::wrmsr(msr, APIC_base_msr);
667
668   // now the CPU feature flags may have changed
669   cpu().update_features_info();
670 }
671
672 // check if we still receive interrupts after we changed the IRQ routing
673 PUBLIC static FIASCO_INIT_CPU
674 int
675 Apic::check_still_getting_interrupts()
676 {
677   if (!Config::apic)
678     return 0;
679
680   Unsigned64 tsc_until;
681   Cpu_time clock_start = Kip::k()->clock;
682
683   tsc_until = Cpu::rdtsc();
684   tsc_until += 0x01000000; // > 10 Mio cycles should be sufficient until
685                            // we have processors with more than 10 GHz
686   do
687     {
688       // kernel clock by timer interrupt updated?
689       if (Kip::k()->clock != clock_start)
690         // yes, succesful
691         return 1;
692     } while (Cpu::rdtsc() < tsc_until);
693
694   // timeout
695   return 0;
696 }
697
698 PUBLIC static
699 inline int
700 Apic::is_present()
701 {
702   return present;
703 }
704
705 PUBLIC static
706 void
707 Apic::set_perf_nmi()
708 {
709   if (have_pcint())
710     reg_write(APIC_lvtpc, 0x400);
711 }
712
713 static FIASCO_INIT_CPU_AND_PM
714 void
715 Apic::calibrate_timer()
716 {
717   const unsigned calibrate_time = 50;
718   Unsigned32 count, tt1, tt2, result, dummy;
719   Unsigned32 runs = 0, frequency_ok;
720
721   do
722     {
723       frequency_khz = 0;
724
725       timer_disable_irq();
726       timer_set_divisor(1);
727       timer_reg_write(1000000000);
728
729         {
730           auto guard = lock_guard(cpu_lock);
731
732           Pit::setup_channel2_to_20hz();
733
734           count = 0;
735
736           tt1 = timer_reg_read();
737           do
738             {
739               count++;
740             }
741           while ((Io::in8(0x61) & 0x20) == 0);
742           tt2 = timer_reg_read();
743         }
744
745       result = (tt1 - tt2) * timer_divisor;
746
747       // APIC not running
748       if (count <= 1)
749         return;
750
751       asm ("divl %2"
752           :"=a" (frequency_khz), "=d" (dummy)
753           : "r" (calibrate_time), "a" (result), "d" (0));
754
755       frequency_ok = (frequency_khz < (1000<<11));
756     }
757   while (++runs < 10 && !frequency_ok);
758
759   if (!frequency_ok)
760     panic("APIC frequency too high, adapt Apic::scaler_us_to_apic");
761
762   Kip::k()->frequency_bus = frequency_khz;
763   scaler_us_to_apic       = Cpu::muldiv(1<<21, frequency_khz, 1000);
764 }
765
766 IMPLEMENT
767 void
768 Apic::error_interrupt(Return_frame *regs)
769 {
770   Unsigned32 err1, err2;
771
772   // we are entering with disabled interrupts
773   err1 = Apic::get_num_errors();
774   Apic::clear_num_errors();
775   err2 = Apic::get_num_errors();
776   Apic::irq_ack();
777
778   cpu_lock.clear();
779
780   if (err1 == 0x80 || err2 == 0x80)
781     {
782       // ignore possible invalid access which may happen in
783       // jdb::do_dump_memory()
784       if (ignore_invalid_apic_reg_access)
785         return;
786
787       printf("CPU%u: APIC invalid register access error at " L4_PTR_FMT "\n",
788              cxx::int_value<Cpu_number>(current_cpu()), regs->ip());
789       return;
790     }
791
792   apic_error_cnt++;
793   printf("CPU%u: APIC error %08x(%08x)\n",
794          cxx::int_value<Cpu_number>(current_cpu()), err1, err2);
795 }
796
797 // deactivate APIC by writing to appropriate MSR
798 PUBLIC static
799 void
800 Apic::done()
801 {
802   Unsigned64 val;
803
804   if (!present)
805     return;
806
807   val = reg_read(APIC_spiv);
808   val &= ~(1<<8);
809   reg_write(APIC_spiv, val);
810
811   val = Cpu::rdmsr(APIC_base_msr);
812   val  &= ~(1<<11);
813   Cpu::wrmsr(val, APIC_base_msr);
814 }
815
816 PRIVATE static FIASCO_INIT_CPU_AND_PM
817 void
818 Apic::init_timer()
819 {
820   calibrate_timer();
821   timer_set_divisor(1);
822   enable_errors();
823 }
824
825 PUBLIC static
826 void
827 Apic::dump_info()
828 {
829   printf("Local APIC[%02x]: version=%02x max_lvt=%d\n",
830          get_id() >> 24, get_version(), get_max_lvt());
831 }
832
833 IMPLEMENT
834 void
835 Apic::init(bool resume)
836 {
837   int was_present;
838   // FIXME: reset cached CPU features, we should add a special function
839   // for the apic bit
840   if(resume)
841     cpu().update_features_info();
842
843   was_present = present = test_present();
844
845   if (!was_present)
846     {
847       good_cpu = test_cpu();
848
849       if (good_cpu && Config::apic)
850         {
851           // activate; this could lead an disabled APIC to appear
852           // set base address of I/O registers to be able to access the registers
853           activate_by_msr();
854           present = test_present();
855         }
856     }
857
858   if (!Config::apic)
859     return;
860
861   // initialize if available
862   if (present)
863     {
864       // map the Local APIC device registers
865       if (!resume)
866         map_apic_page();
867
868       // set some interrupt vectors to appropriate values
869       init_lvt();
870
871       // initialize APIC_spiv register
872       init_spiv();
873
874       // initialize task-priority register
875       init_tpr();
876
877       // test if local timer counts down
878       if ((present = check_working()))
879         {
880           if (!was_present)
881             // APIC _was_ not present before writing to msr so we have
882             // to set APIC_lvt0 and APIC_lvt1 to appropriate values
883             route_pic_through_apic();
884         }
885     }
886
887   if (!present)
888     panic("Local APIC not found");
889
890   dump_info();
891
892   apic_io_base = Mem_layout::Local_apic_page;
893   init_timer();
894 }