]> rtime.felk.cvut.cz Git - l4.git/blobdiff - kernel/fiasco/src/kern/ia32/apic-ia32.cpp
Update
[l4.git] / kernel / fiasco / src / kern / ia32 / apic-ia32.cpp
index b35e56c823c7e16019285d254a25f54ed906a0f7..a015c376bc5ea9becf556ddd897697a053ee646a 100644 (file)
@@ -1,27 +1,29 @@
 INTERFACE:
 
+#include "per_cpu_data.h"
 #include "types.h"
 #include "initcalls.h"
+#include "pm.h"
 
 class Return_frame;
 class Cpu;
 
-class Apic
+class Apic : public Pm_object
 {
 public:
-  static void init() FIASCO_INIT;
+  static void init(bool resume = false) FIASCO_INIT_AND_PM;
+  Unsigned32 apic_id() const { return _id; }
+  Unsigned32 cpu_id() const { return _id >> 24; }
 
-  typedef enum
-  {
-    APIC_NONE,
-    APIC_P6,                   // Intel PPro, PIII
-    APIC_P4,                   // Intel PIV
-    APIC_K7                    // AMD Athlon Model 2
-  } Apic_type;
+  static Per_cpu<Static_object<Apic> > apic;
 
 private:
-  Apic();                      // default constructors are undefined
-  Apic(const Apic&);
+  Apic(const Apic&) = delete;
+  Apic &operator = (Apic const &) = delete;
+
+  Unsigned32 _id;
+  Unsigned32 _saved_apic_timer;
+
   static void                  error_interrupt(Return_frame *regs)
                                asm ("apic_error_interrupt") FIASCO_FASTCALL;
 
@@ -30,7 +32,6 @@ private:
   static const                 Address io_base;
   static Address               phys_base;
   static unsigned              timer_divisor;
-  static Apic_type             type;
   static unsigned              frequency_khz;
   static Unsigned64            scaler_us_to_apic;
 
@@ -42,7 +43,7 @@ private:
     APIC_tpri_mask             = 0xFF,
     APIC_eoi                   = 0xB0,
     APIC_ldr                   = 0xD0,
-    APIC_ldr_mask              = (0xFF<<24),
+    APIC_ldr_mask              = 0xFFul << 24,
     APIC_dfr                   = 0xE0,
     APIC_spiv                  = 0xF0,
     APIC_isr                   = 0x100,
@@ -60,12 +61,12 @@ private:
     APIC_tmcct                 = 0x390,
     APIC_tdcr                  = 0x3E0,
 
-    APIC_snd_pending           = (1<<12),
-    APIC_input_polarity                = (1<<13),
-    APIC_lvt_remote_irr                = (1<<14),
-    APIC_lvt_level_trigger     = (1<<15),
-    APIC_lvt_masked            = (1<<16),
-    APIC_lvt_timer_periodic    = (1<<17),
+    APIC_snd_pending           = 1 << 12,
+    APIC_input_polarity                = 1 << 13,
+    APIC_lvt_remote_irr                = 1 << 14,
+    APIC_lvt_level_trigger     = 1 << 15,
+    APIC_lvt_masked            = 1 << 16,
+    APIC_lvt_timer_periodic    = 1 << 17,
     APIC_tdr_div_1             = 0xB,
     APIC_tdr_div_2             = 0x0,
     APIC_tdr_div_4             = 0x1,
@@ -101,10 +102,62 @@ extern unsigned apic_error_cnt;
 IMPLEMENTATION:
 #include "cpu.h"
 
+DEFINE_PER_CPU Per_cpu<Static_object<Apic> >  Apic::apic;
+
+PUBLIC inline
+Apic::Apic(Cpu_number cpu) : _id(get_id()) { register_pm(cpu); }
+
+
 PRIVATE static
 Cpu &
 Apic::cpu() { return *Cpu::boot_cpu(); }
 
+// FIXME: workaround for missing lambdas in gcc < 4.5
+namespace {
+struct By_id
+{
+  Unsigned32 p;
+  By_id(Unsigned32 p) : p(p) {}
+  bool operator () (Apic const *a) const { return a && a->apic_id() == p; }
+};
+}
+
+PUBLIC static
+Cpu_number
+Apic::find_cpu(Unsigned32 phys_id)
+{
+  return apic.find_cpu(By_id(phys_id));
+}
+
+PUBLIC void
+Apic::pm_on_suspend(Cpu_number)
+{
+  _saved_apic_timer = timer_reg_read();
+}
+
+//----------------------------------------------------------------------------
+IMPLEMENTATION [!mp]:
+
+PUBLIC void
+Apic::pm_on_resume(Cpu_number)
+{
+  Apic::init(true);
+  timer_reg_write(_saved_apic_timer);
+}
+
+//----------------------------------------------------------------------------
+IMPLEMENTATION [mp]:
+
+PUBLIC void
+Apic::pm_on_resume(Cpu_number cpu)
+{
+  if (cpu == Cpu_number::boot_cpu())
+    Apic::init(true);
+  else
+    Apic::init_ap();
+  timer_reg_write(_saved_apic_timer);
+}
+
 
 //----------------------------------------------------------------------------
 IMPLEMENTATION[ia32]:
@@ -174,7 +227,6 @@ int        Apic::good_cpu;
 const Address Apic::io_base = Mem_layout::Local_apic_page;
 Address    Apic::phys_base;
 unsigned   Apic::timer_divisor = 1;
-Apic::Apic_type Apic::type = APIC_NONE;
 unsigned   Apic::frequency_khz;
 Unsigned64 Apic::scaler_us_to_apic;
 
@@ -212,6 +264,7 @@ PRIVATE static inline NOEXPORT
 Unsigned32
 Apic::get_num_errors()
 {
+  reg_write(APIC_esr, 0);
   return reg_read(APIC_esr);
 }
 
@@ -220,6 +273,7 @@ void
 Apic::clear_num_errors()
 {
   reg_write(APIC_esr, 0);
+  reg_write(APIC_esr, 0);
 }
 
 PUBLIC static inline
@@ -286,12 +340,12 @@ Apic::apic_page_phys()
 
 // set the global pagetable entry for the Local APIC device registers
 PUBLIC
-static
+static FIASCO_INIT_AND_PM
 void
 Apic::map_apic_page()
 {
   Address offs;
-  Address base = Cpu::rdmsr(APIC_base_msr) & 0xfffff000;
+  Address base = apic_page_phys();
   // We should not change the physical address of the Local APIC page if
   // possible since some versions of VMware would complain about a
   // non-implemented feature
@@ -304,7 +358,7 @@ Apic::map_apic_page()
 }
 
 // check CPU type if APIC could be present
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
 int
 Apic::test_cpu()
 {
@@ -314,24 +368,12 @@ Apic::test_cpu()
   if (cpu().vendor() == Cpu::Vendor_intel)
     {
       if (cpu().family() == 15)
-       {
-         // Intel PIV
-         type = APIC_P4;
-         return 1;
-       }
-      else if (cpu().family() >= 6)
-       {
-         // Intel PPro, PIII
-         type = APIC_P6;
-         return 1;
-       }
+       return 1;
+      if (cpu().family() >= 6)
+       return 1;
     }
   if (cpu().vendor() == Cpu::Vendor_amd && cpu().family() >= 6)
-    {
-      // >= AMD K7 Model 2
-      type = APIC_K7;
-      return 1;
-    }
+    return 1;
 
   return 0;
 }
@@ -430,7 +472,7 @@ Apic::timer_set_divisor(unsigned newdiv)
        {
          if (divval & ~1)
            {
-             printf("bad APIC divisor %d\n", newdiv);
+             printf("bad APIC divisor %u\n", newdiv);
              return;
            }
          div = divisor_tab[i];
@@ -471,7 +513,7 @@ Apic::have_tsint()
 }
 
 // check if APIC is working (check timer functionality)
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
 int
 Apic::check_working()
 {
@@ -480,24 +522,24 @@ Apic::check_working()
   timer_disable_irq();
   timer_set_divisor(1);
   timer_reg_write(0x10000000);
-  
-  tsc_until = Cpu::rdtsc() + 0x100;  // we only have to wait for one bus cycle
 
-  do 
+  tsc_until = Cpu::rdtsc() + 0x400;  // we only have to wait for one bus cycle
+
+  do
     {
       if (timer_reg_read() != 0x10000000)
-       return 1;
+        return 1;
     } while (Cpu::rdtsc() < tsc_until);
 
   return 0;
 }
 
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::init_spiv()
 {
   Unsigned32 tmp_val;
-  
+
   tmp_val = reg_read(APIC_spiv);
   tmp_val |= (1<<8);            // enable APIC
   tmp_val &= ~(1<<9);           // enable Focus Processor Checking
@@ -517,15 +559,15 @@ unsigned
 Apic::tpr()
 { return reg_read(APIC_tpr); }
 
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::init_tpr()
 {
-  reg_write (APIC_tpr, 0);
+  reg_write(APIC_tpr, 0);
 }
 
 // activate APIC error interrupt
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::enable_errors()
 {
@@ -545,7 +587,7 @@ Apic::enable_errors()
       if (get_max_lvt() > 3)
        clear_num_errors();
       after = get_num_errors();
-      printf("APIC ESR value before/after enabling: %08x/%08x\n", 
+      printf("APIC ESR value before/after enabling: %08x/%08x\n",
            before, after);
     }
 }
@@ -553,22 +595,22 @@ Apic::enable_errors()
 // activate APIC after activating by MSR was successful
 // see "Intel Architecture Software Developer's Manual,
 //      Volume 3: System Programming Guide, Appendix E"
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
 void
 Apic::route_pic_through_apic()
 {
   Unsigned32 tmp_val;
-  Lock_guard <Cpu_lock> guard(&cpu_lock);
+  auto guard = lock_guard(cpu_lock);
 
   // mask 8259 interrupts
-  Pic::Status old_irqs = Pic::disable_all_save();
+  Unsigned16 old_irqs = Pic::disable_all_save();
 
   // set LINT0 to ExtINT, edge triggered
   tmp_val = reg_read(APIC_lvt0);
   tmp_val &= 0xfffe5800;
   tmp_val |= 0x00000700;
   reg_write(APIC_lvt0, tmp_val);
-    
+
   // set LINT1 to NMI, edge triggered
   tmp_val = reg_read(APIC_lvt1);
   tmp_val &= 0xfffe5800;
@@ -581,34 +623,22 @@ Apic::route_pic_through_apic()
   printf("APIC was disabled --- routing PIC through APIC\n");
 }
 
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::init_lvt()
 {
-  Unsigned32 tmp_val;
-  Lock_guard <Cpu_lock> guard(&cpu_lock);
+  auto guard = lock_guard(cpu_lock);
 
   // mask timer interrupt and set vector to _not_ invalid value
-  tmp_val  = reg_read(APIC_lvtt);
-  tmp_val |= APIC_lvt_masked;
-  tmp_val |= 0xff;
-  reg_write(APIC_lvtt, tmp_val);
+  reg_write(APIC_lvtt, reg_read(APIC_lvtt) | APIC_lvt_masked | 0xff);
+
   if (have_pcint())
-    {
-      // mask performance interrupt and set vector to a valid value
-      tmp_val  = reg_read(APIC_lvtpc);
-      tmp_val |= APIC_lvt_masked;
-      tmp_val |= 0xff;
-      reg_write(APIC_lvtpc, tmp_val);
-    }
+    // mask performance interrupt and set vector to a valid value
+    reg_write(APIC_lvtpc, reg_read(APIC_lvtpc) | APIC_lvt_masked | 0xff);
+
   if (have_tsint())
-    {
-      // mask thermal sensor interrupt and set vector to a valid value
-      tmp_val  = reg_read(APIC_lvtthmr);
-      tmp_val |= APIC_lvt_masked;
-      tmp_val |= 0xff;
-      reg_write(APIC_lvtthmr, tmp_val);
-    }
+    // mask thermal sensor interrupt and set vector to a valid value
+    reg_write(APIC_lvtthmr, reg_read(APIC_lvtthmr) | APIC_lvt_masked | 0xff);
 }
 
 // give us a hint if we have an APIC but it is disabled
@@ -624,7 +654,7 @@ Apic::test_present_but_disabled()
 }
 
 // activate APIC by writing to appropriate MSR
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::activate_by_msr()
 {
@@ -636,7 +666,7 @@ Apic::activate_by_msr()
   Cpu::wrmsr(msr, APIC_base_msr);
 
   // now the CPU feature flags may have changed
-  cpu().identify();
+  cpu().update_features_info();
 }
 
 // check if we still receive interrupts after we changed the IRQ routing
@@ -660,7 +690,7 @@ Apic::check_still_getting_interrupts()
        // yes, succesful
        return 1;
     } while (Cpu::rdtsc() < tsc_until);
-  
+
   // timeout
   return 0;
 }
@@ -672,13 +702,6 @@ Apic::is_present()
   return present;
 }
 
-PUBLIC static
-inline int
-Apic::cpu_type()
-{
-  return type;
-}
-
 PUBLIC static
 void
 Apic::set_perf_nmi()
@@ -687,7 +710,7 @@ Apic::set_perf_nmi()
     reg_write(APIC_lvtpc, 0x400);
 }
 
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::calibrate_timer()
 {
@@ -695,7 +718,7 @@ Apic::calibrate_timer()
   Unsigned32 count, tt1, tt2, result, dummy;
   Unsigned32 runs = 0, frequency_ok;
 
-  do 
+  do
     {
       frequency_khz = 0;
 
@@ -704,7 +727,7 @@ Apic::calibrate_timer()
       timer_reg_write(1000000000);
 
         {
-          Lock_guard <Cpu_lock> guard(&cpu_lock);
+          auto guard = lock_guard(cpu_lock);
 
           Pit::setup_channel2_to_20hz();
 
@@ -761,13 +784,14 @@ Apic::error_interrupt(Return_frame *regs)
       if (ignore_invalid_apic_reg_access)
        return;
 
-      printf("APIC invalid register access error at "L4_PTR_FMT"\n",
-            regs->ip());
+      printf("CPU%u: APIC invalid register access error at " L4_PTR_FMT "\n",
+            cxx::int_value<Cpu_number>(current_cpu()), regs->ip());
       return;
     }
 
   apic_error_cnt++;
-  printf("APIC error %08x(%08x)\n", err1, err2);
+  printf("CPU%u: APIC error %08x(%08x)\n",
+         cxx::int_value<Cpu_number>(current_cpu()), err1, err2);
 }
 
 // deactivate APIC by writing to appropriate MSR
@@ -789,7 +813,7 @@ Apic::done()
   Cpu::wrmsr(val, APIC_base_msr);
 }
 
-PRIVATE static FIASCO_INIT_CPU
+PRIVATE static FIASCO_INIT_CPU_AND_PM
 void
 Apic::init_timer()
 {
@@ -804,55 +828,60 @@ Apic::dump_info()
 {
   printf("Local APIC[%02x]: version=%02x max_lvt=%d\n",
          get_id() >> 24, get_version(), get_max_lvt());
-
 }
 
 IMPLEMENT
 void
-Apic::init()
+Apic::init(bool resume)
 {
   int was_present;
+  // FIXME: reset cached CPU features, we should add a special function
+  // for the apic bit
+  if(resume)
+    cpu().update_features_info();
+
+  was_present = present = test_present();
 
-  good_cpu = test_cpu();
+  if (!was_present)
+    {
+      good_cpu = test_cpu();
+
+      if (good_cpu && Config::apic)
+        {
+          // activate; this could lead an disabled APIC to appear
+          // set base address of I/O registers to be able to access the registers
+          activate_by_msr();
+          present = test_present();
+        }
+    }
 
   if (!Config::apic)
     return;
 
-  // check CPU type
-  if ((present = good_cpu))
+  // initialize if available
+  if (present)
     {
-      // check cpu features of cpuid
-      was_present = test_present();
-
-      // activate; this could lead an disabled APIC to appear
-      // set base address of I/O registers to be able to access the registers
-      activate_by_msr();
+      // map the Local APIC device registers
+      if (!resume)
+        map_apic_page();
 
-      // previous test_present() could have failed but we could have it
-      // activated by writing the msr so we have to test again
-      if ((present = test_present()))
-       {
-         // map the Local APIC device registers
-         map_apic_page();
+      // set some interrupt vectors to appropriate values
+      init_lvt();
 
-         // set some interrupt vectors to appropriate values
-         init_lvt();
+      // initialize APIC_spiv register
+      init_spiv();
 
-         // initialize APIC_spiv register
-         init_spiv();
+      // initialize task-priority register
+      init_tpr();
 
-         // initialize task-priority register
-         init_tpr();
-
-         // test if local timer counts down
-         if ((present = check_working()))
-           {
-             if (!was_present)
-               // APIC _was_ not present before writing to msr so we have
-               // to set APIC_lvt0 and APIC_lvt1 to appropriate values
-               route_pic_through_apic();
-           }
-       }
+      // test if local timer counts down
+      if ((present = check_working()))
+        {
+          if (!was_present)
+            // APIC _was_ not present before writing to msr so we have
+            // to set APIC_lvt0 and APIC_lvt1 to appropriate values
+            route_pic_through_apic();
+        }
     }
 
   if (!present)