]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/apic-ia32.cpp
fiasco: Access APIC with instructions understood by jailhouse
[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   Unsigned32 val;
291   asm volatile ("mov %1,%0\n" : "=b" (val) : "m" (*(Unsigned32*)(io_base + reg)) : "memory");
292   return val;
293 }
294
295 PUBLIC static inline
296 void
297 Apic::reg_write(unsigned reg, Unsigned32 val)
298 {
299   asm volatile ("mov %1,%0\n" : "=m" (*(Unsigned32*)(io_base + reg)) : "b" (val) : "memory");
300 }
301
302 PUBLIC static inline
303 int
304 Apic::reg_delivery_mode(Unsigned32 val)
305 {
306   return (val >> 8) & 7;
307 }
308
309 PUBLIC static inline
310 int
311 Apic::reg_lvt_vector(Unsigned32 val)
312 {
313   return val & 0xff;
314 }
315
316 PUBLIC static inline
317 Unsigned32
318 Apic::timer_reg_read()
319 {
320   return reg_read(APIC_tmcct);
321 }
322
323 PUBLIC static inline
324 Unsigned32
325 Apic::timer_reg_read_initial()
326 {
327   return reg_read(APIC_tmict);
328 }
329
330 PUBLIC static inline
331 void
332 Apic::timer_reg_write(Unsigned32 val)
333 {
334   reg_read(APIC_tmict);
335   reg_write(APIC_tmict, val);
336 }
337
338 PUBLIC static inline NEEDS["cpu.h"]
339 Address
340 Apic::apic_page_phys()
341 { return Cpu::rdmsr(APIC_base_msr) & 0xfffff000; }
342
343 // set the global pagetable entry for the Local APIC device registers
344 PUBLIC
345 static FIASCO_INIT_AND_PM
346 void
347 Apic::map_apic_page()
348 {
349   Address offs;
350   Address base = apic_page_phys();
351   // We should not change the physical address of the Local APIC page if
352   // possible since some versions of VMware would complain about a
353   // non-implemented feature
354   Kmem::map_phys_page(base, Mem_layout::Local_apic_page,
355                       false, true, &offs);
356
357   Kip::k()->add_mem_region(Mem_desc(base, base + Config::PAGE_SIZE - 1, Mem_desc::Reserved));
358
359   assert(offs == 0);
360 }
361
362 // check CPU type if APIC could be present
363 static FIASCO_INIT_AND_PM
364 int
365 Apic::test_cpu()
366 {
367   if (!cpu().can_wrmsr() || !(cpu().features() & FEAT_TSC))
368     return 0;
369
370   if (cpu().vendor() == Cpu::Vendor_intel)
371     {
372       if (cpu().family() == 15)
373         return 1;
374       if (cpu().family() >= 6)
375         return 1;
376     }
377   if (cpu().vendor() == Cpu::Vendor_amd && cpu().family() >= 6)
378     return 1;
379
380   return 0;
381 }
382
383 // test if APIC present
384 static inline
385 int
386 Apic::test_present()
387 {
388   return cpu().features() & FEAT_APIC;
389 }
390
391 PUBLIC static inline
392 void
393 Apic::timer_enable_irq()
394 {
395   Unsigned32 tmp_val;
396
397   tmp_val = reg_read(APIC_lvtt);
398   tmp_val &= ~(APIC_lvt_masked);
399   reg_write(APIC_lvtt, tmp_val);
400 }
401
402 PUBLIC static inline
403 void
404 Apic::timer_disable_irq()
405 {
406   Unsigned32 tmp_val;
407
408   tmp_val = reg_read(APIC_lvtt);
409   tmp_val |= APIC_lvt_masked;
410   reg_write(APIC_lvtt, tmp_val);
411 }
412
413 PUBLIC static inline
414 int
415 Apic::timer_is_irq_enabled()
416 {
417   return ~reg_read(APIC_lvtt) & APIC_lvt_masked;
418 }
419
420 PUBLIC static inline
421 void
422 Apic::timer_set_periodic()
423 {
424   Unsigned32 tmp_val = reg_read(APIC_lvtt);
425   tmp_val |= APIC_lvt_timer_periodic;
426   reg_write(APIC_lvtt, tmp_val);
427 }
428
429 PUBLIC static inline
430 void
431 Apic::timer_set_one_shot()
432 {
433   Unsigned32 tmp_val = reg_read(APIC_lvtt);
434   tmp_val &= ~APIC_lvt_timer_periodic;
435   reg_write(APIC_lvtt, tmp_val);
436 }
437
438 PUBLIC static inline
439 void
440 Apic::timer_assign_irq_vector(unsigned vector)
441 {
442   Unsigned32 tmp_val = reg_read(APIC_lvtt);
443   tmp_val &= 0xffffff00;
444   tmp_val |= vector;
445   reg_write(APIC_lvtt, tmp_val);
446 }
447
448 PUBLIC static inline
449 void
450 Apic::irq_ack()
451 {
452   reg_read(APIC_spiv);
453   reg_write(APIC_eoi, 0);
454 }
455
456 static
457 void
458 Apic::timer_set_divisor(unsigned newdiv)
459 {
460   int i;
461   int div = -1;
462   int divval = newdiv;
463   Unsigned32 tmp_value;
464
465   static int divisor_tab[8] =
466     {
467       APIC_tdr_div_1,  APIC_tdr_div_2,  APIC_tdr_div_4,  APIC_tdr_div_8,
468       APIC_tdr_div_16, APIC_tdr_div_32, APIC_tdr_div_64, APIC_tdr_div_128
469     };
470
471   for (i=0; i<8; i++)
472     {
473       if (divval & 1)
474         {
475           if (divval & ~1)
476             {
477               printf("bad APIC divisor %u\n", newdiv);
478               return;
479             }
480           div = divisor_tab[i];
481           break;
482         }
483       divval >>= 1;
484     }
485
486   if (div != -1)
487     {
488       timer_divisor = newdiv;
489       tmp_value = reg_read(APIC_tdcr);
490       tmp_value &= ~0x1F;
491       tmp_value |= div;
492       reg_write(APIC_tdcr, tmp_value);
493     }
494 }
495
496 static
497 int
498 Apic::get_max_lvt()
499 {
500   return is_integrated() ? get_max_lvt_local() : 2;
501 }
502
503 PUBLIC static inline
504 int
505 Apic::have_pcint()
506 {
507   return (present && (get_max_lvt() >= 4));
508 }
509
510 PUBLIC static inline
511 int
512 Apic::have_tsint()
513 {
514   return (present && (get_max_lvt() >= 5));
515 }
516
517 // check if APIC is working (check timer functionality)
518 static FIASCO_INIT_AND_PM
519 int
520 Apic::check_working()
521 {
522   Unsigned64 tsc_until;
523
524   timer_disable_irq();
525   timer_set_divisor(1);
526   timer_reg_write(0x10000000);
527
528   tsc_until = Cpu::rdtsc() + 0x400;  // we only have to wait for one bus cycle
529
530   do
531     {
532       if (timer_reg_read() != 0x10000000)
533         return 1;
534     } while (Cpu::rdtsc() < tsc_until);
535
536   return 0;
537 }
538
539 static FIASCO_INIT_CPU_AND_PM
540 void
541 Apic::init_spiv()
542 {
543   Unsigned32 tmp_val;
544
545   tmp_val = reg_read(APIC_spiv);
546   tmp_val |= (1<<8);            // enable APIC
547   tmp_val &= ~(1<<9);           // enable Focus Processor Checking
548   tmp_val &= ~0xff;
549   tmp_val |= APIC_IRQ_BASE + 0xf; // Set spurious IRQ vector to 0x3f
550                               // bit 0..3 are hardwired to 1 on PPro!
551   reg_write(APIC_spiv, tmp_val);
552 }
553
554 PUBLIC static inline NEEDS[Apic::reg_write]
555 void
556 Apic::tpr(unsigned prio)
557 { reg_write(APIC_tpr, prio); }
558
559 PUBLIC static inline NEEDS[Apic::reg_read]
560 unsigned
561 Apic::tpr()
562 { return reg_read(APIC_tpr); }
563
564 static FIASCO_INIT_CPU_AND_PM
565 void
566 Apic::init_tpr()
567 {
568   reg_write(APIC_tpr, 0);
569 }
570
571 // activate APIC error interrupt
572 static FIASCO_INIT_CPU_AND_PM
573 void
574 Apic::enable_errors()
575 {
576   if (is_integrated())
577     {
578       Unsigned32 tmp_val, before, after;
579
580       if (get_max_lvt() > 3)
581         clear_num_errors();
582       before = get_num_errors();
583
584       tmp_val = reg_read(APIC_lvterr);
585       tmp_val &= 0xfffeff00;         // unmask error IRQ vector
586       tmp_val |= APIC_IRQ_BASE + 3;  // Set error IRQ vector to 0x63
587       reg_write(APIC_lvterr, tmp_val);
588
589       if (get_max_lvt() > 3)
590         clear_num_errors();
591       after = get_num_errors();
592       printf("APIC ESR value before/after enabling: %08x/%08x\n",
593             before, after);
594     }
595 }
596
597 // activate APIC after activating by MSR was successful
598 // see "Intel Architecture Software Developer's Manual,
599 //      Volume 3: System Programming Guide, Appendix E"
600 static FIASCO_INIT_AND_PM
601 void
602 Apic::route_pic_through_apic()
603 {
604   Unsigned32 tmp_val;
605   auto guard = lock_guard(cpu_lock);
606
607   // mask 8259 interrupts
608   Unsigned16 old_irqs = Pic::disable_all_save();
609
610   // set LINT0 to ExtINT, edge triggered
611   tmp_val = reg_read(APIC_lvt0);
612   tmp_val &= 0xfffe5800;
613   tmp_val |= 0x00000700;
614   reg_write(APIC_lvt0, tmp_val);
615
616   // set LINT1 to NMI, edge triggered
617   tmp_val = reg_read(APIC_lvt1);
618   tmp_val &= 0xfffe5800;
619   tmp_val |= 0x00000400;
620   reg_write(APIC_lvt1, tmp_val);
621
622   // unmask 8259 interrupts
623   Pic::restore_all(old_irqs);
624
625   printf("APIC was disabled --- routing PIC through APIC\n");
626 }
627
628 static FIASCO_INIT_CPU_AND_PM
629 void
630 Apic::init_lvt()
631 {
632   auto guard = lock_guard(cpu_lock);
633
634   // mask timer interrupt and set vector to _not_ invalid value
635   reg_write(APIC_lvtt, reg_read(APIC_lvtt) | APIC_lvt_masked | 0xff);
636
637   if (have_pcint())
638     // mask performance interrupt and set vector to a valid value
639     reg_write(APIC_lvtpc, reg_read(APIC_lvtpc) | APIC_lvt_masked | 0xff);
640
641   if (have_tsint())
642     // mask thermal sensor interrupt and set vector to a valid value
643     reg_write(APIC_lvtthmr, reg_read(APIC_lvtthmr) | APIC_lvt_masked | 0xff);
644 }
645
646 // give us a hint if we have an APIC but it is disabled
647 PUBLIC static
648 int
649 Apic::test_present_but_disabled()
650 {
651   if (!good_cpu)
652     return 0;
653
654   Unsigned64 msr = Cpu::rdmsr(APIC_base_msr);
655   return ((msr & 0xffffff000ULL) == 0xfee00000ULL);
656 }
657
658 // activate APIC by writing to appropriate MSR
659 static FIASCO_INIT_CPU_AND_PM
660 void
661 Apic::activate_by_msr()
662 {
663   Unsigned64 msr;
664
665   msr = Cpu::rdmsr(APIC_base_msr);
666   phys_base = msr & 0xfffff000;
667   msr |= (1<<11);
668   Cpu::wrmsr(msr, APIC_base_msr);
669
670   // now the CPU feature flags may have changed
671   cpu().update_features_info();
672 }
673
674 // check if we still receive interrupts after we changed the IRQ routing
675 PUBLIC static FIASCO_INIT_CPU
676 int
677 Apic::check_still_getting_interrupts()
678 {
679   if (!Config::apic)
680     return 0;
681
682   Unsigned64 tsc_until;
683   Cpu_time clock_start = Kip::k()->clock;
684
685   tsc_until = Cpu::rdtsc();
686   tsc_until += 0x01000000; // > 10 Mio cycles should be sufficient until
687                            // we have processors with more than 10 GHz
688   do
689     {
690       // kernel clock by timer interrupt updated?
691       if (Kip::k()->clock != clock_start)
692         // yes, succesful
693         return 1;
694     } while (Cpu::rdtsc() < tsc_until);
695
696   // timeout
697   return 0;
698 }
699
700 PUBLIC static
701 inline int
702 Apic::is_present()
703 {
704   return present;
705 }
706
707 PUBLIC static
708 void
709 Apic::set_perf_nmi()
710 {
711   if (have_pcint())
712     reg_write(APIC_lvtpc, 0x400);
713 }
714
715 static FIASCO_INIT_CPU_AND_PM
716 void
717 Apic::calibrate_timer()
718 {
719   const unsigned calibrate_time = 50;
720   Unsigned32 count, tt1, tt2, result, dummy;
721   Unsigned32 runs = 0, frequency_ok;
722
723   do
724     {
725       frequency_khz = 0;
726
727       timer_disable_irq();
728       timer_set_divisor(1);
729       timer_reg_write(1000000000);
730
731         {
732           auto guard = lock_guard(cpu_lock);
733
734           Pit::setup_channel2_to_20hz();
735
736           count = 0;
737
738           tt1 = timer_reg_read();
739           do
740             {
741               count++;
742             }
743           while ((Io::in8(0x61) & 0x20) == 0);
744           tt2 = timer_reg_read();
745         }
746
747       result = (tt1 - tt2) * timer_divisor;
748
749       // APIC not running
750       if (count <= 1)
751         return;
752
753       asm ("divl %2"
754           :"=a" (frequency_khz), "=d" (dummy)
755           : "r" (calibrate_time), "a" (result), "d" (0));
756
757       frequency_ok = (frequency_khz < (1000<<11));
758     }
759   while (++runs < 10 && !frequency_ok);
760
761   if (!frequency_ok)
762     panic("APIC frequency too high, adapt Apic::scaler_us_to_apic");
763
764   Kip::k()->frequency_bus = frequency_khz;
765   scaler_us_to_apic       = Cpu::muldiv(1<<21, frequency_khz, 1000);
766 }
767
768 IMPLEMENT
769 void
770 Apic::error_interrupt(Return_frame *regs)
771 {
772   Unsigned32 err1, err2;
773
774   // we are entering with disabled interrupts
775   err1 = Apic::get_num_errors();
776   Apic::clear_num_errors();
777   err2 = Apic::get_num_errors();
778   Apic::irq_ack();
779
780   cpu_lock.clear();
781
782   if (err1 == 0x80 || err2 == 0x80)
783     {
784       // ignore possible invalid access which may happen in
785       // jdb::do_dump_memory()
786       if (ignore_invalid_apic_reg_access)
787         return;
788
789       printf("CPU%u: APIC invalid register access error at " L4_PTR_FMT "\n",
790              cxx::int_value<Cpu_number>(current_cpu()), regs->ip());
791       return;
792     }
793
794   apic_error_cnt++;
795   printf("CPU%u: APIC error %08x(%08x)\n",
796          cxx::int_value<Cpu_number>(current_cpu()), err1, err2);
797 }
798
799 // deactivate APIC by writing to appropriate MSR
800 PUBLIC static
801 void
802 Apic::done()
803 {
804   Unsigned64 val;
805
806   if (!present)
807     return;
808
809   val = reg_read(APIC_spiv);
810   val &= ~(1<<8);
811   reg_write(APIC_spiv, val);
812
813   val = Cpu::rdmsr(APIC_base_msr);
814   val  &= ~(1<<11);
815   Cpu::wrmsr(val, APIC_base_msr);
816 }
817
818 PRIVATE static FIASCO_INIT_CPU_AND_PM
819 void
820 Apic::init_timer()
821 {
822   calibrate_timer();
823   timer_set_divisor(1);
824   enable_errors();
825 }
826
827 PUBLIC static
828 void
829 Apic::dump_info()
830 {
831   printf("Local APIC[%02x]: version=%02x max_lvt=%d\n",
832          get_id() >> 24, get_version(), get_max_lvt());
833 }
834
835 IMPLEMENT
836 void
837 Apic::init(bool resume)
838 {
839   int was_present;
840   // FIXME: reset cached CPU features, we should add a special function
841   // for the apic bit
842   if(resume)
843     cpu().update_features_info();
844
845   was_present = present = test_present();
846
847   if (!was_present)
848     {
849       good_cpu = test_cpu();
850
851       if (good_cpu && Config::apic)
852         {
853           // activate; this could lead an disabled APIC to appear
854           // set base address of I/O registers to be able to access the registers
855           activate_by_msr();
856           present = test_present();
857         }
858     }
859
860   if (!Config::apic)
861     return;
862
863   // initialize if available
864   if (present)
865     {
866       // map the Local APIC device registers
867       if (!resume)
868         map_apic_page();
869
870       // set some interrupt vectors to appropriate values
871       init_lvt();
872
873       // initialize APIC_spiv register
874       init_spiv();
875
876       // initialize task-priority register
877       init_tpr();
878
879       // test if local timer counts down
880       if ((present = check_working()))
881         {
882           if (!was_present)
883             // APIC _was_ not present before writing to msr so we have
884             // to set APIC_lvt0 and APIC_lvt1 to appropriate values
885             route_pic_through_apic();
886         }
887     }
888
889   if (!present)
890     panic("Local APIC not found");
891
892   dump_info();
893
894   apic_io_base = Mem_layout::Local_apic_page;
895   init_timer();
896 }