Unsigned32 _brand;
Unsigned32 _features;
Unsigned32 _ext_features;
+ Unsigned32 _ext_07_ebx;
Unsigned32 _ext_8000_0001_ecx;
Unsigned32 _ext_8000_0001_edx;
Unsigned32 _local_features;
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;
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; }
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; }
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(); }
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:
#include "initcalls.h"
#include "per_cpu_data.h"
#include "gdt.h"
+#include "lock_guard.h"
+#include "spin_lock.h"
class Gdt;
class Tss;
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();
#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;
{ 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, "" }
};
{ 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, "" }
};
};
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);
}
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
{
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))
return 0;
Unsigned32 dummy, dummy1, dummy2, features;
- cpuid (1, &dummy, &dummy1, &dummy2, &features);
+ cpuid(1, &dummy, &dummy1, &dummy2, &features);
return features;
}
// 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");
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())
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:
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';
}
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
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)
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]:
#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
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()
calibrate_tsc();
- if (scaler_tsc_to_ns)
- _frequency = ns_to_tsc(1000000000UL);
-
Unsigned32 cr4 = get_cr4();
if (features() & FEAT_FXSR)
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;
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();
}
while ((Io::in8 (0x61) & 0x20) == 0);
tsc_end = rdtsc ();
-
- // restore flags
- Proc::sti_restore(o);
}
// Error: ECTCNEVERSET
:"=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");
}
}
+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;
-}