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