]> rtime.felk.cvut.cz Git - l4.git/blobdiff - kernel/fiasco/src/kern/ia32/cpu-ia32.cpp
update
[l4.git] / kernel / fiasco / src / kern / ia32 / cpu-ia32.cpp
index 07d468d053bd9c95799cdd873eb03439bb030b37..b17d5158055f78bed42b8aacc7cce22ba09d2e58 100644 (file)
@@ -63,6 +63,7 @@ private:
   Unsigned32 _brand;
   Unsigned32 _features;
   Unsigned32 _ext_features;
+  Unsigned32 _ext_07_ebx;
   Unsigned32 _ext_8000_0001_ecx;
   Unsigned32 _ext_8000_0001_edx;
   Unsigned32 _local_features;
@@ -96,12 +97,12 @@ private:
   Unsigned32 _l3_cache_size;
   Unsigned16 _l3_cache_asso;
   Unsigned16 _l3_cache_line_size;
-  
+
   Unsigned8 _phys_bits;
   Unsigned8 _virt_bits;
 
   Vendor _vendor;
-  char _model_str[32];
+  char _model_str[52];
 
   Unsigned32 _arch_perfmon_info_eax;
   Unsigned32 _arch_perfmon_info_ebx;
@@ -118,7 +119,7 @@ private:
 
 public:
 
-  void disable(unsigned cpu, char const *reason);
+  void disable(Cpu_number cpu, char const *reason);
 
   char const *model_str() const { return _model_str; }
   Vendor vendor() const { return _vendor; }
@@ -140,6 +141,10 @@ public:
   unsigned ext_features() const { return _ext_features; }
   bool has_monitor_mwait() const { return _ext_features & (1 << 3); }
   bool has_monitor_mwait_irq() const { return _monitor_mwait_ecx & 3; }
+
+  bool __attribute__((const)) has_smep() const
+  { return _ext_07_ebx & FEATX_SMEP; }
+
   unsigned ext_8000_0001_ecx() const { return _ext_8000_0001_ecx; }
   unsigned ext_8000_0001_edx() const { return _ext_8000_0001_edx; }
   unsigned local_features() const { return _local_features; }
@@ -159,7 +164,7 @@ public:
   Address volatile &kernel_sp() const;
 
 public:
-  static Per_cpu<Cpu> cpus asm ("CPUS_BASE");
+  static Per_cpu<Cpu> cpus;
   static Cpu *boot_cpu() { return _boot_cpu; }
 
   static bool have_superpages() { return boot_cpu()->superpages(); }
@@ -167,6 +172,9 @@ public:
   static bool have_syscall() { return boot_cpu()->syscall(); }
   static bool have_fxsr() { return boot_cpu()->features() & FEAT_FXSR; }
   static bool have_pge() { return boot_cpu()->features() & FEAT_PGE; }
+  static bool have_xsave() { return boot_cpu()->ext_features() & FEATX_XSAVE; }
+
+  bool has_xsave() const { return ext_features() & FEATX_XSAVE; }
 
 private:
 
@@ -218,6 +226,8 @@ INTERFACE [ia32, amd64]:
 #include "initcalls.h"
 #include "per_cpu_data.h"
 #include "gdt.h"
+#include "lock_guard.h"
+#include "spin_lock.h"
 
 class Gdt;
 class Tss;
@@ -291,7 +301,7 @@ public:
     Gdt::set (&desc);
   }
 
-  void set_tss() const { set_tr(Gdt::gdt_tss); }
+  static void set_tss() { set_tr(Gdt::gdt_tss); }
 
 private:
   void init_lbr_type();
@@ -308,7 +318,7 @@ IMPLEMENTATION[ia32,amd64,ux]:
 #include "panic.h"
 #include "processor.h"
 
-Per_cpu<Cpu> DEFINE_PER_CPU_P(0) Cpu::cpus(true);
+DEFINE_PER_CPU_P(0) Per_cpu<Cpu> Cpu::cpus(Per_cpu_data::Cpu_num);
 Cpu *Cpu::_boot_cpu;
 
 
@@ -370,7 +380,6 @@ Cpu::Vendor_table const Cpu::intel_table[] FIASCO_INITDATA_CPU =
   { 0xf0ff0, 0x106a0, 0xffff, "Core i7 / Xeon, 45nm"            },
   { 0xf0ff0, 0x106b0, 0xffff, "Xeon MP, 45nm"                   },
   { 0xf0ff0, 0x106c0, 0xffff, "Atom"                            },
-  { 0xf0ff0, 0x206c0, 0xffff, "Xeon X5680 (and others?)"        },
   { 0x0,     0x0,     0xFFFF, ""                                }
 };
 
@@ -426,8 +435,6 @@ Cpu::Vendor_table const Cpu::amd_table[] FIASCO_INITDATA_CPU =
   { 0xfff0ff0,  0x040f30,   0,      "Athlon 64 X2 (Windsor)"       },
   { 0xfff0ff0,  0x040f80,   0,      "Turion 64 X2 (Taylor)"        },
   { 0xfff0ff0,  0x060fb0,   0,      "Athlon 64 X2 (Brisbane)"      },
-  { 0xfff0ff0,  0x100f20,   0,      "Phenom X3/Toliman / X4/Agena" },
-  { 0xfff0ff0,  0x100f40,   0,      "Opteron (Shanghai)"           },
   { 0x0,        0x0,        0,      ""                             }
 };
 
@@ -665,12 +672,13 @@ char const * const Cpu::exception_strings[] =
 };
 
 PUBLIC explicit FIASCO_INIT_CPU
-Cpu::Cpu(unsigned cpu)
+Cpu::Cpu(Cpu_number cpu)
 {
   set_id(cpu);
-  if (cpu == 0)
+  if (cpu == Cpu_number::boot_cpu())
     {
       _boot_cpu = this;
+      set_present(1);
       set_online(1);
     }
 
@@ -692,17 +700,32 @@ Cpu::exception_string(Mword trapno)
   return exception_strings[trapno];
 }
 
-PUBLIC static inline FIASCO_INIT_CPU
+PUBLIC static inline FIASCO_INIT_CPU_AND_PM
 void
-Cpu::cpuid(Unsigned32 const mode,
-           Unsigned32 *const eax, Unsigned32 *const ebx,
-           Unsigned32 *const ecx, Unsigned32 *const edx)
+Cpu::cpuid(Unsigned32 mode,
+           Unsigned32 *eax, Unsigned32 *ebx,
+           Unsigned32 *ecx, Unsigned32 *edx)
 {
   asm volatile ("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
                         : "a" (mode));
 }
 
+PUBLIC static inline FIASCO_INIT_CPU
+void
+Cpu::cpuid(Unsigned32 mode, Unsigned32 ecx_val,
+           Unsigned32 *eax, Unsigned32 *ebx,
+           Unsigned32 *ecx, Unsigned32 *edx)
+{
+  asm volatile ("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
+                        : "a" (mode), "c" (ecx_val));
+}
 
+PUBLIC
+void
+Cpu::update_features_info()
+{
+  cpuid(1, &_version, &_brand, &_ext_features, &_features);
+}
 
 PRIVATE FIASCO_INIT_CPU
 void
@@ -864,6 +887,9 @@ Cpu::set_model_str()
 {
   Vendor_table const *table;
 
+  if (_model_str[0])
+    return;
+
   for (table = vendor_table[vendor()]; table && table->vendor_mask; table++)
     if ((_version & table->vendor_mask) == table->vendor_code &&
         (table->l2_cache == 0xFFFF || _l2_cache_size >= table->l2_cache))
@@ -911,7 +937,7 @@ Cpu::get_features()
     return 0;
 
   Unsigned32 dummy, dummy1, dummy2, features;
-  cpuid (1, &dummy, &dummy1, &dummy2, &features);
+  cpuid(1, &dummy, &dummy1, &dummy2, &features);
 
   return features;
 }
@@ -942,6 +968,7 @@ Cpu::identify()
 
   // Check for Alignment Check Support
   set_flags(eflags ^ EFLAGS_AC);
+  // FIXME: must not panic at cpu hotplug
   if (((get_flags() ^ eflags) & EFLAGS_AC) == 0)
     panic("CPU too old");
 
@@ -974,13 +1001,19 @@ Cpu::identify()
         if (_vendor == Vendor_intel)
           cache_tlb_intel();
       case 1:
-        cpuid(1, &_version, &_brand, &_ext_features, &_features);
+        update_features_info();
       }
 
     if (max >= 5 && has_monitor_mwait())
       cpuid(5, &_monitor_mwait_eax, &_monitor_mwait_ebx,
                &_monitor_mwait_ecx, &_monitor_mwait_edx);
 
+    if (max >= 7 && _vendor == Vendor_intel)
+      {
+        Unsigned32 dummy1, dummy2, dummy3;
+        cpuid(0x7, 0, &dummy1, &_ext_07_ebx, &dummy2, &dummy3);
+      }
+
     if (_vendor == Vendor_intel)
       {
        switch (family())
@@ -1034,6 +1067,13 @@ Cpu::identify()
            if (_vendor == Vendor_amd || _vendor == Vendor_via)
              cache_tlb_l1();
          case 0x80000004:
+           {
+             Unsigned32 *s = (Unsigned32 *)_model_str;
+             for (unsigned i = 0; i < 3; ++i)
+               cpuid(0x80000002 + i, &s[0 + 4*i], &s[1 + 4*i],
+                                      &s[2 + 4*i], &s[3 + 4*i]);
+             _model_str[48] = 0;
+           }
          case 0x80000003:
          case 0x80000002:
          case 0x80000001:
@@ -1067,11 +1107,25 @@ Cpu::busy_wait_ns(Unsigned64 ns)
     Proc::pause();
 }
 
+PUBLIC
+bool
+Cpu::if_show_infos() const
+{
+  return    id() == Cpu_number::boot_cpu()
+         || !boot_cpu()
+         || family()    != boot_cpu()->family()
+         || model()     != boot_cpu()->model()
+         || stepping()  != boot_cpu()->stepping()
+         || brand()     != boot_cpu()->brand();
+}
 
 PUBLIC
 void
 Cpu::show_cache_tlb_info(const char *indent) const
 {
+  if (!if_show_infos())
+    return;
+
   char s[16];
 
   *s = '\0';
@@ -1128,10 +1182,10 @@ Cpu::show_cache_tlb_info(const char *indent) const
 }
 
 IMPLEMENT
-void 
-Cpu::disable(unsigned cpu, char const *reason)
+void
+Cpu::disable(Cpu_number cpu, char const *reason)
 {
-  printf("CPU%u: is disabled: %s\n", cpu, reason);
+  printf("CPU%u: is disabled: %s\n", cxx::int_value<Cpu_number>(cpu), reason);
 }
 
 // Function used for calculating apic scaler
@@ -1184,15 +1238,6 @@ Cpu::get_ss()
   return val;
 }
 
-PUBLIC static inline
-Unsigned32
-Cpu::get_fs()
-{
-  Unsigned32 val;
-  asm volatile ("mov %%fs, %0" : "=rm" (val));
-  return val;
-}
-
 PUBLIC static inline
 void
 Cpu::set_ds(Unsigned32 val)
@@ -1203,12 +1248,6 @@ void
 Cpu::set_es(Unsigned32 val)
 { asm volatile ("mov %0, %%es" : : "rm" (val)); }
 
-PUBLIC static inline
-void
-Cpu::set_fs(Unsigned32 val)
-{ asm volatile ("mov %0, %%fs" : : "rm" (val)); }
-
-
 //----------------------------------------------------------------------------
 IMPLEMENTATION[ia32, amd64]:
 
@@ -1224,6 +1263,44 @@ IMPLEMENTATION[ia32, amd64]:
 #include "regdefs.h"
 #include "tss.h"
 
+EXTENSION class Cpu
+{
+private:
+  Unsigned64 _suspend_tsc;
+};
+
+PUBLIC FIASCO_INIT_AND_PM
+void
+Cpu::pm_suspend()
+{
+  Gdt_entry tss_entry;
+  tss_entry = (*gdt)[Gdt::gdt_tss / 8];
+  tss_entry.access &= 0xfd;
+  (*gdt)[Gdt::gdt_tss / 8] = tss_entry;
+  _suspend_tsc = rdtsc();
+}
+
+PUBLIC FIASCO_INIT_AND_PM
+void
+Cpu::pm_resume()
+{
+  if (id() != Cpu_number::boot_cpu())
+    {
+      // the boot CPU restores some state in asm already
+      set_gdt();
+      set_ldt(0);
+      set_ds(Gdt::data_segment());
+      set_es(Gdt::data_segment());
+      set_ss(Gdt::gdt_data_kernel | Gdt::Selector_kernel);
+      set_fs(Gdt::gdt_data_user   | Gdt::Selector_user);
+      set_gs(Gdt::gdt_data_user   | Gdt::Selector_user);
+      set_cs();
+
+      set_tss();
+    }
+  init_sysenter();
+  wrmsr(_suspend_tsc, MSR_TSC);
+}
 
 PUBLIC static inline
 void
@@ -1256,6 +1333,27 @@ void
 Cpu::set_tr(Unsigned16 val)
 { asm volatile ("ltr %0" : : "rm" (val)); }
 
+PUBLIC static inline
+void
+Cpu::xsetbv(Unsigned64 val, Unsigned32 xcr)
+{
+  asm volatile ("xsetbv" : : "a" ((Mword)val),
+                             "d" ((Mword)(val >> 32)),
+                             "c" (xcr));
+}
+
+PUBLIC static inline
+Unsigned64
+Cpu::xgetbv(Unsigned32 xcr)
+{
+  Unsigned32 eax, edx;
+  asm volatile("xgetbv"
+               : "=a" (eax),
+                 "=d" (edx)
+               : "c" (xcr));
+  return eax | ((Unsigned64)edx << 32);
+}
+
 PUBLIC static inline
 Mword
 Cpu::get_cr0()
@@ -1505,9 +1603,6 @@ Cpu::init()
 
   calibrate_tsc();
 
-  if (scaler_tsc_to_ns)
-    _frequency = ns_to_tsc(1000000000UL);
-
   Unsigned32 cr4 = get_cr4();
 
   if (features() & FEAT_FXSR)
@@ -1516,69 +1611,40 @@ Cpu::init()
   if (features() & FEAT_SSE)
     cr4 |= CR4_OSXMMEXCPT;
 
+  // enable SMEP if available
+  if (has_smep())
+    cr4 |= CR4_SMEP;
+
   set_cr4 (cr4);
 
   // reset time stamp counter (better for debugging)
   if ((features() & FEAT_TSC) && can_wrmsr())
-    wrmsr(0, 0, 0x10);
+    wrmsr(0, 0, MSR_TSC);
 
   if ((features() & FEAT_PAT) && can_wrmsr())
-    wrmsr(0x00010406, 0x00070406, 0x277);
+    wrmsr(0x00010406, 0x00070406, MSR_PAT);
 
   print_errata();
 }
 
 PUBLIC
 void
-Cpu::print() const
-{
-  printf ("CPU[%u:%u]: %s (%X:%X:%X:%X)[%08x] Model: %s at %llu MHz\n\n",
-          id(), phys_id() >> 24,
-          vendor_str(), family(), model(), stepping(), brand(), _version, model_str(),
-          div32(frequency(), 1000000));
-}
-
-PUBLIC
-void
-Cpu::set_sysenter(void (*func)(void))
-{
-  // Check for Sysenter/Sysexit Feature
-  if (sysenter())
-    wrmsr ((Mword) func, 0, MSR_SYSENTER_EIP);
-}
-
-
-PUBLIC
-void
-Cpu::set_fast_entry(void (*func)(void))
+Cpu::print_infos() const
 {
-  set_sysenter(func);
+  if (if_show_infos())
+    printf("CPU[%u]: %s (%X:%X:%X:%X)[%08x] Model: %s at %lluMHz\n\n",
+           cxx::int_value<Cpu_number>(id()),
+           vendor_str(), family(), model(), stepping(), brand(),
+           _version, model_str(),
+           div32(frequency(), 1000000));
+
+  show_cache_tlb_info("");
 }
 
-extern "C" void entry_sys_fast_ipc (void);
-extern "C" void entry_sys_fast_ipc_c (void);
-
-PUBLIC FIASCO_INIT_CPU
-void
-Cpu::init_sysenter()
-{
-  // Check for Sysenter/Sysexit Feature
-  if (sysenter())
-    {
-      wrmsr (Gdt::gdt_code_kernel, 0, MSR_SYSENTER_CS);
-      wrmsr ((unsigned long)&kernel_sp(), 0, MSR_SYSENTER_ESP);
-      if (Config::Assembler_ipc_shortcut)
-       set_sysenter(entry_sys_fast_ipc);
-      else
-       set_sysenter(entry_sys_fast_ipc_c);
-    }
-}
-
-
 // Return 2^32 / (tsc clocks per usec)
 FIASCO_INIT_CPU
 void
-Cpu::calibrate_tsc ()
+Cpu::calibrate_tsc()
 {
   const unsigned calibrate_time = 50000 /*us*/ + 1;
 
@@ -1586,12 +1652,16 @@ Cpu::calibrate_tsc ()
   if (! (features() & FEAT_TSC))
     goto bad_ctc;
 
+  // only do once
+  if (scaler_tsc_to_ns)
+    return;
+
   Unsigned64 tsc_start, tsc_end;
   Unsigned32 count, tsc_to_ns_div, dummy;
 
     {
-      // disable interrupts
-      Proc::Status o = Proc::cli_save();
+      static Spin_lock<> _l;
+      auto guard = lock_guard(_l);
 
       Pit::setup_channel2_to_20hz();
 
@@ -1603,9 +1673,6 @@ Cpu::calibrate_tsc ()
        }
       while ((Io::in8 (0x61) & 0x20) == 0);
       tsc_end = rdtsc ();
-
-      // restore flags
-      Proc::sti_restore(o);
     }
 
   // Error: ECTCNEVERSET
@@ -1627,15 +1694,22 @@ Cpu::calibrate_tsc ()
        :"=a" (tsc_to_ns_div), "=d" (dummy)
        :"r" ((Unsigned32)tsc_end), "a" (0), "d" (calibrate_time));
 
-  scaler_tsc_to_ns = muldiv (tsc_to_ns_div, 1000, 1<<5);
-  scaler_tsc_to_us =         tsc_to_ns_div;
-  scaler_ns_to_tsc = muldiv (1<<31, ((Unsigned32)tsc_end),
-                            calibrate_time * 1000>>1 * 1<<5);
+  // scaler_tsc_to_ns = (tsc_to_ns_div * 1000) / 32
+  // not using muldiv(tsc_to_ns_div, 1000, 1 << 5), as div result > (1 << 32)
+  // will get trap0 if system frequency is too low
+  scaler_tsc_to_ns  = tsc_to_ns_div * 31;
+  scaler_tsc_to_ns += tsc_to_ns_div / 4;
+  scaler_tsc_to_us  = tsc_to_ns_div;
+  scaler_ns_to_tsc  = muldiv(1 << 31, ((Unsigned32)tsc_end),
+                             calibrate_time * 1000 >> 1 * 1 << 5);
+
+  if (scaler_tsc_to_ns)
+    _frequency = ns_to_tsc(1000000000UL);
 
   return;
 
 bad_ctc:
-  if (Config::kinfo_timer_uses_rdtsc)
+  if (Config::Kip_timer_uses_rdtsc)
     panic("Can't calibrate tsc");
 }
 
@@ -1664,26 +1738,23 @@ Cpu::enable_ldt(Address addr, int size)
 }
 
 
+PUBLIC static inline
+Unsigned32
+Cpu::get_fs()
+{ Unsigned32 val; asm volatile ("mov %%fs, %0" : "=rm" (val)); return val; }
+
 PUBLIC static inline
 Unsigned32
 Cpu::get_gs()
 { Unsigned32 val; asm volatile ("mov %%gs, %0" : "=rm" (val)); return val; }
 
+PUBLIC static inline
+void
+Cpu::set_fs(Unsigned32 val)
+{ asm volatile ("mov %0, %%fs" : : "rm" (val)); }
+
 PUBLIC static inline
 void
 Cpu::set_gs(Unsigned32 val)
 { asm volatile ("mov %0, %%gs" : : "rm" (val)); }
 
-IMPLEMENT inline
-unsigned
-Cpu::phys_id() const
-{ return _brand & 0xff000000; }
-
-PUBLIC static
-unsigned
-Cpu::phys_id_direct()
-{
-  Unsigned32 a,b,c,d;
-  cpuid (1, &a, &b, &c, &d);
-  return b & 0xff000000;
-}