]> rtime.felk.cvut.cz Git - l4.git/blobdiff - kernel/fiasco/src/kern/ia32/apic-ia32.cpp
fiasco: Improved access to APIC through Jailhosue. Read and write instructions are...
[l4.git] / kernel / fiasco / src / kern / ia32 / apic-ia32.cpp
index e3a6b9fca5a01e9344c80d54d51a9e4d7029043b..5ddb759172062f1e7b2ce588d7d40a9804ba9fa8 100644 (file)
@@ -1,19 +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; }
+
+  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;
 
@@ -33,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,
@@ -51,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,
@@ -92,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]:
@@ -225,14 +287,20 @@ PUBLIC static inline
 Unsigned32
 Apic::reg_read(unsigned reg)
 {
-  return *((volatile Unsigned32*)(io_base + reg));
+  Unsigned32 val;
+  /* assembly-encoded to match the Jailhouse hypervisor MMIO parser support */
+  void *address = (void*)(/*(Unsigned32*)*/(io_base + reg));
+  asm volatile("mov (%1),%0" : "=r" (val) : "r" (address));
+  return val;
 }
 
 PUBLIC static inline
 void
 Apic::reg_write(unsigned reg, Unsigned32 val)
 {
-  *((volatile Unsigned32*)(io_base + reg)) = val;
+  /* assembly-encoded to match the Jailhouse hypervisor MMIO parser support */
+  void *address = (void*)(/*(Unsigned32*)*/(io_base + reg));
+  asm volatile("mov %0,(%1)" : : "r" (val), "r" (address));
 }
 
 PUBLIC static inline
@@ -278,7 +346,7 @@ 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()
 {
@@ -296,7 +364,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()
 {
@@ -410,7 +478,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];
@@ -451,7 +519,7 @@ Apic::have_tsint()
 }
 
 // check if APIC is working (check timer functionality)
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
 int
 Apic::check_working()
 {
@@ -460,24 +528,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
@@ -497,15 +565,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()
 {
@@ -533,22 +601,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;
@@ -561,34 +629,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
@@ -604,7 +660,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()
 {
@@ -616,7 +672,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 +716,7 @@ Apic::set_perf_nmi()
     reg_write(APIC_lvtpc, 0x400);
 }
 
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
 void
 Apic::calibrate_timer()
 {
@@ -677,7 +733,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();
 
@@ -734,13 +790,14 @@ Apic::error_interrupt(Return_frame *regs)
       if (ignore_invalid_apic_reg_access)
        return;
 
-      printf("cpu%d: APIC invalid register access error at "L4_PTR_FMT"\n",
-            current_cpu(), 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("cpu%d: APIC error %08x(%08x)\n", current_cpu(), 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
@@ -762,7 +819,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()
 {
@@ -781,9 +838,13 @@ Apic::dump_info()
 
 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();
 
@@ -807,7 +868,8 @@ Apic::init()
   if (present)
     {
       // map the Local APIC device registers
-      map_apic_page();
+      if (!resume)
+        map_apic_page();
 
       // set some interrupt vectors to appropriate values
       init_lvt();