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