1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-20 17:55:08 +00:00

Kernel: Consolidate features into CPUFeature enum

This allows us to consolidate printing out all the CPU features
into one log statement. Also expose them in /proc/cpuinfo
This commit is contained in:
Tom 2020-07-03 10:23:09 -06:00 committed by Andreas Kling
parent e373e5f007
commit 9b4e6f6a23
9 changed files with 197 additions and 145 deletions

View file

@ -26,6 +26,7 @@
#include <AK/Assertions.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/Types.h>
#include <Kernel/Arch/i386/CPU.h>
#include <Kernel/Arch/i386/ProcessorInfo.h>
@ -633,7 +634,7 @@ void exit_trap(TrapFrame* trap)
return Processor::current().exit_trap(*trap);
}
void sse_init()
static void sse_init()
{
asm volatile(
"mov %cr0, %eax\n"
@ -645,122 +646,6 @@ void sse_init()
"mov %eax, %cr4\n");
}
bool g_cpu_supports_nx;
bool g_cpu_supports_pae;
bool g_cpu_supports_pge;
bool g_cpu_supports_rdrand;
bool g_cpu_supports_rdseed;
bool g_cpu_supports_smap;
bool g_cpu_supports_smep;
bool g_cpu_supports_sse;
bool g_cpu_supports_tsc;
bool g_cpu_supports_umip;
void cpu_detect()
{
CPUID processor_info(0x1);
g_cpu_supports_pae = (processor_info.edx() & (1 << 6));
g_cpu_supports_pge = (processor_info.edx() & (1 << 13));
g_cpu_supports_sse = (processor_info.edx() & (1 << 25));
g_cpu_supports_tsc = (processor_info.edx() & (1 << 4));
g_cpu_supports_rdrand = (processor_info.ecx() & (1 << 30));
CPUID extended_processor_info(0x80000001);
g_cpu_supports_nx = (extended_processor_info.edx() & (1 << 20));
CPUID extended_features(0x7);
g_cpu_supports_smap = (extended_features.ebx() & (1 << 20));
g_cpu_supports_smep = (extended_features.ebx() & (1 << 7));
g_cpu_supports_umip = (extended_features.ecx() & (1 << 2));
g_cpu_supports_rdseed = (extended_features.ebx() & (1 << 18));
}
void cpu_setup(u32 cpu)
{
if (cpu == 0)
cpu_detect();
if (g_cpu_supports_sse) {
sse_init();
klog() << "x86: SSE support enabled";
}
asm volatile(
"movl %%cr0, %%eax\n"
"orl $0x00010000, %%eax\n"
"movl %%eax, %%cr0\n" ::
: "%eax", "memory");
klog() << "x86: WP support enabled";
if (g_cpu_supports_pge) {
// Turn on CR4.PGE so the CPU will respect the G bit in page tables.
asm volatile(
"mov %cr4, %eax\n"
"orl $0x80, %eax\n"
"mov %eax, %cr4\n");
klog() << "x86: PGE support enabled";
} else {
klog() << "x86: PGE support not detected";
}
if (g_cpu_supports_nx) {
// Turn on IA32_EFER.NXE
asm volatile(
"movl $0xc0000080, %ecx\n"
"rdmsr\n"
"orl $0x800, %eax\n"
"wrmsr\n");
klog() << "x86: NX support enabled";
} else {
klog() << "x86: NX support not detected";
}
if (g_cpu_supports_smep) {
// Turn on CR4.SMEP
asm volatile(
"mov %cr4, %eax\n"
"orl $0x100000, %eax\n"
"mov %eax, %cr4\n");
klog() << "x86: SMEP support enabled";
} else {
klog() << "x86: SMEP support not detected";
}
if (g_cpu_supports_smap) {
// Turn on CR4.SMAP
klog() << "x86: Enabling SMAP";
asm volatile(
"mov %cr4, %eax\n"
"orl $0x200000, %eax\n"
"mov %eax, %cr4\n");
klog() << "x86: SMAP support enabled";
} else {
klog() << "x86: SMAP support not detected";
}
if (g_cpu_supports_umip) {
asm volatile(
"mov %cr4, %eax\n"
"orl $0x800, %eax\n"
"mov %eax, %cr4\n");
klog() << "x86: UMIP support enabled";
}
if (g_cpu_supports_tsc) {
asm volatile(
"mov %cr4, %eax\n"
"orl $0x4, %eax\n"
"mov %eax, %cr4\n");
klog() << "x86: RDTSC support restricted";
}
if (g_cpu_supports_rdrand) {
klog() << "x86: Using RDRAND for good randomness";
} else {
klog() << "x86: No RDRAND support detected, randomness will be poor";
}
}
u32 read_cr0()
{
u32 cr0;
@ -822,6 +707,156 @@ Processor& Processor::by_id(u32 cpu)
return *procs[cpu];
}
void Processor::cpu_detect()
{
// NOTE: This is called during Processor::early_initialize, we cannot
// safely log at this point because we don't have kmalloc
// initialized yet!
auto set_feature =
[&](CPUFeature f) {
m_features = static_cast<CPUFeature>(static_cast<u32>(m_features) | static_cast<u32>(f));
};
m_features = static_cast<CPUFeature>(0);
CPUID processor_info(0x1);
if (processor_info.edx() & (1 << 6))
set_feature(CPUFeature::PAE);
if (processor_info.edx() & (1 << 13))
set_feature(CPUFeature::PGE);
if (processor_info.edx() & (1 << 25))
set_feature(CPUFeature::SSE);
if (processor_info.edx() & (1 << 4))
set_feature(CPUFeature::TSC);
if (processor_info.ecx() & (1 << 30))
set_feature(CPUFeature::RDRAND);
CPUID extended_processor_info(0x80000001);
if (extended_processor_info.edx() & (1 << 20))
set_feature(CPUFeature::NX);
CPUID extended_features(0x7);
if (extended_features.ebx() & (1 << 20))
set_feature(CPUFeature::SMAP);
if (extended_features.ebx() & (1 << 7))
set_feature(CPUFeature::SMEP);
if (extended_features.ecx() & (1 << 2))
set_feature(CPUFeature::UMIP);
if (extended_features.ebx() & (1 << 18))
set_feature(CPUFeature::RDSEED);
}
void Processor::cpu_setup()
{
// NOTE: This is called during Processor::early_initialize, we cannot
// safely log at this point because we don't have kmalloc
// initialized yet!
cpu_detect();
if (has_feature(CPUFeature::SSE))
sse_init();
asm volatile(
"movl %%cr0, %%eax\n"
"orl $0x00010000, %%eax\n"
"movl %%eax, %%cr0\n" ::
: "%eax", "memory");
if (has_feature(CPUFeature::PGE)) {
// Turn on CR4.PGE so the CPU will respect the G bit in page tables.
asm volatile(
"mov %cr4, %eax\n"
"orl $0x80, %eax\n"
"mov %eax, %cr4\n");
}
if (has_feature(CPUFeature::NX)) {
// Turn on IA32_EFER.NXE
asm volatile(
"movl $0xc0000080, %ecx\n"
"rdmsr\n"
"orl $0x800, %eax\n"
"wrmsr\n");
}
if (has_feature(CPUFeature::SMEP)) {
// Turn on CR4.SMEP
asm volatile(
"mov %cr4, %eax\n"
"orl $0x100000, %eax\n"
"mov %eax, %cr4\n");
}
if (has_feature(CPUFeature::SMAP)) {
// Turn on CR4.SMAP
asm volatile(
"mov %cr4, %eax\n"
"orl $0x200000, %eax\n"
"mov %eax, %cr4\n");
}
if (has_feature(CPUFeature::UMIP)) {
asm volatile(
"mov %cr4, %eax\n"
"orl $0x800, %eax\n"
"mov %eax, %cr4\n");
}
if (has_feature(CPUFeature::TSC)) {
asm volatile(
"mov %cr4, %eax\n"
"orl $0x4, %eax\n"
"mov %eax, %cr4\n");
}
}
String Processor::features_string() const
{
StringBuilder builder;
auto feature_to_str =
[](CPUFeature f) -> const char*
{
switch (f) {
case CPUFeature::NX:
return "nx";
case CPUFeature::PAE:
return "pae";
case CPUFeature::PGE:
return "pge";
case CPUFeature::RDRAND:
return "rdrand";
case CPUFeature::RDSEED:
return "rdseed";
case CPUFeature::SMAP:
return "smap";
case CPUFeature::SMEP:
return "smep";
case CPUFeature::SSE:
return "sse";
case CPUFeature::TSC:
return "tsc";
case CPUFeature::UMIP:
return "umip";
// no default statement here intentionally so that we get
// a warning if a new feature is forgotten to be added here
}
// Shouldn't ever happen
return "???";
};
bool first = true;
for (u32 flag = 1; flag < sizeof(m_features) * 8; flag <<= 1) {
if ((static_cast<u32>(m_features) & flag) != 0) {
if (first)
first = false;
else
builder.append(' ');
auto str = feature_to_str(static_cast<CPUFeature>(flag));
builder.append(str, strlen(str));
}
}
return builder.build();
}
void Processor::early_initialize(u32 cpu)
{
m_self = this;
@ -838,6 +873,8 @@ void Processor::early_initialize(u32 cpu)
m_mm_data = nullptr;
m_info = nullptr;
cpu_setup();
gdt_init();
ASSERT(&current() == this); // sanity check
}
@ -847,12 +884,9 @@ void Processor::initialize(u32 cpu)
ASSERT(m_self == this);
ASSERT(&current() == this); // sanity check
m_cpu = cpu;
m_in_irq = 0;
m_idle_thread = nullptr;
m_current_thread = nullptr;
m_mm_data = nullptr;
klog() << "CPU[" << id() << "]: Supported features: " << features_string();
if (!has_feature(CPUFeature::RDRAND))
klog() << "CPU[" << id() << "]: No RDRAND support detected, randomness will be poor";
if (cpu == 0)
idt_init();

View file

@ -265,7 +265,6 @@ struct RegisterState;
const DescriptorTablePointer& get_gdtr();
const DescriptorTablePointer& get_idtr();
void sse_init();
void register_interrupt_handler(u8 number, void (*f)());
void register_user_callable_interrupt_handler(u8 number, void (*f)());
GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number);
@ -599,6 +598,19 @@ private:
SplitQword m_start;
};
enum class CPUFeature : u32 {
NX = (1 << 0),
PAE = (1 << 1),
PGE = (1 << 2),
RDRAND = (1 << 3),
RDSEED = (1 << 4),
SMAP = (1 << 5),
SMEP = (1 << 6),
SSE = (1 << 7),
TSC = (1 << 8),
UMIP = (1 << 9)
};
class Thread;
struct TrapFrame;
@ -614,6 +626,8 @@ class ProcessorInfo;
struct MemoryManagerData;
class Processor {
friend class ProcessorInfo;
Processor* m_self; // must be first field (%fs offset 0x0)
DescriptorTablePointer m_gdtr;
@ -626,6 +640,7 @@ class Processor {
TSS32 m_tss;
static FPUState s_clean_fpu_state;
CPUFeature m_features;
ProcessorInfo* m_info;
MemoryManagerData* m_mm_data;
@ -640,6 +655,11 @@ class Processor {
void write_gdt_entry(u16 selector, Descriptor& descriptor);
static Vector<Processor*>& processors();
void cpu_detect();
void cpu_setup();
String features_string() const;
public:
void early_initialize(u32 cpu);
void initialize(u32 cpu);
@ -740,6 +760,11 @@ public:
return s_clean_fpu_state;
}
ALWAYS_INLINE bool has_feature(CPUFeature f) const
{
return (static_cast<u32>(m_features) & static_cast<u32>(f)) != 0;
}
void check_invoke_scheduler();
void invoke_scheduler_async() { m_invoke_scheduler_async = true; }
@ -846,22 +871,9 @@ public:
}
};
void cpu_setup(u32 cpu);
extern bool g_cpu_supports_nx;
extern bool g_cpu_supports_pae;
extern bool g_cpu_supports_pge;
extern bool g_cpu_supports_rdrand;
extern bool g_cpu_supports_rdseed;
extern bool g_cpu_supports_smap;
extern bool g_cpu_supports_smep;
extern bool g_cpu_supports_sse;
extern bool g_cpu_supports_tsc;
extern bool g_cpu_supports_umip;
ALWAYS_INLINE void stac()
{
if (!g_cpu_supports_smap)
if (!Processor::current().has_feature(CPUFeature::SMAP))
return;
asm volatile("stac" ::
: "cc");
@ -869,7 +881,7 @@ ALWAYS_INLINE void stac()
ALWAYS_INLINE void clac()
{
if (!g_cpu_supports_smap)
if (!Processor::current().has_feature(CPUFeature::SMAP))
return;
asm volatile("clac" ::
: "cc");

View file

@ -88,6 +88,9 @@ ProcessorInfo::ProcessorInfo(Processor& processor):
copy_brand_string_part_to_buffer(2);
m_brandstr = buffer;
}
// Cache the CPU feature string
m_features = m_processor.features_string();
}
}

View file

@ -37,6 +37,7 @@ class ProcessorInfo
Processor& m_processor;
String m_cpuid;
String m_brandstr;
String m_features;
u32 m_display_model;
u32 m_display_family;
u32 m_stepping;
@ -47,6 +48,7 @@ public:
const String& cpuid() const { return m_cpuid; }
const String& brandstr() const { return m_brandstr; }
const String& features() const { return m_features; }
u32 display_model() const { return m_display_model; }
u32 display_family() const { return m_display_family; }
u32 stepping() const { return m_stepping; }

View file

@ -773,6 +773,7 @@ Optional<KBuffer> procfs$cpuinfo(InodeIdentifier)
builder.appendf("processor: %u\n", proc.id());
builder.appendf("cpuid: %s\n", info.cpuid().characters());
builder.appendf("family: %u\n", info.display_family());
builder.appendf("features: %s\n", info.features().characters());
builder.appendf("model: %u\n", info.display_model());
builder.appendf("stepping: %u\n", info.stepping());
builder.appendf("type: %u\n", info.type());

View file

@ -44,10 +44,12 @@ KernelRng& KernelRng::the()
KernelRng::KernelRng()
{
if (g_cpu_supports_rdseed || g_cpu_supports_rdrand) {
bool supports_rdseed = Processor::current().has_feature(CPUFeature::RDSEED);
bool supports_rdrand = Processor::current().has_feature(CPUFeature::RDRAND);
if (supports_rdseed || supports_rdrand) {
for (size_t i = 0; i < resource().pool_count * resource().reseed_threshold; ++i) {
u32 value = 0;
if (g_cpu_supports_rdseed) {
if (supports_rdseed) {
asm volatile(
"1:\n"
"rdseed %0\n"

View file

@ -80,7 +80,7 @@ void MemoryManager::protect_kernel_image()
pte.set_writable(false);
}
if (g_cpu_supports_nx) {
if (Processor::current().has_feature(CPUFeature::NX)) {
// Disable execution of the kernel data and bss segments.
for (size_t i = (FlatPtr)&start_of_kernel_data; i < (FlatPtr)&end_of_kernel_bss; i += PAGE_SIZE) {
auto& pte = ensure_pte(kernel_page_directory(), VirtualAddress(i));

View file

@ -237,7 +237,7 @@ void Region::map_individual_page_impl(size_t page_index)
pte.set_writable(false);
else
pte.set_writable(is_writable());
if (g_cpu_supports_nx)
if (Processor::current().has_feature(CPUFeature::NX))
pte.set_execute_disabled(!is_executable());
pte.set_user_allowed(is_user_accessible());
#ifdef MM_DEBUG

View file

@ -106,7 +106,6 @@ extern "C" [[noreturn]] void init()
setup_serial_debug();
s_bsp_processor.early_initialize(0);
cpu_setup(0);
kmalloc_init();
slab_alloc_init();
@ -169,7 +168,6 @@ extern "C" [[noreturn]] void init_ap(u32 cpu, Processor* processor_info)
klog() << "CPU #" << cpu << " processor_info at " << VirtualAddress(FlatPtr(processor_info));
cpu_setup(cpu);
processor_info->initialize(cpu);
MemoryManager::initialize(cpu);