blob: 1594252a1c22b841920bfbae1b9008ffdeb55e95 [file] [log] [blame]
/*
* Copyright (c) 2009 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*/
#include <arch/arch.h>
#include <arch/x86.h>
#include <arch/mmu.h>
#include <stdio.h>
#include <assert.h>
#include <ros/memlayout.h>
#include <pmap.h>
#include <kdebug.h>
#include <string.h>
#include <cpu_feat.h>
int x86_family, x86_model, x86_stepping;
/* Check Intel's SDM 2a for Table 3-17 for the cpuid leaves */
void print_cpuinfo(void)
{
uint32_t eax, ebx, ecx, edx;
uint32_t model, family, ext_model, ext_family;
uint64_t msr_val;
char vendor_id[13];
int max_std_lvl, max_extd_lvl;
extern char _start[];
if (sizeof(long) == 8)
printk("64 bit Kernel Booting...\n");
else
printk("32 bit Kernel Booting...\n");
// x86 Vendor Detection:
asm volatile ("cpuid;"
"movl %%ebx, (%2);"
"movl %%edx, 4(%2);"
"movl %%ecx, 8(%2);"
: "=a"(eax)
: "a"(0), "D"(vendor_id)
: "%ebx", "%ecx", "%edx");
vendor_id[12] = '\0';
cprintf("Vendor ID: %s\n", vendor_id);
/* not a great check - old intel P5s have no vendor id */
if (!strcmp(vendor_id, "GenuineIntel"))
cpu_set_feat(CPU_FEAT_X86_VENDOR_INTEL);
else if (!strcmp(vendor_id, "AuthenticAMD"))
cpu_set_feat(CPU_FEAT_X86_VENDOR_AMD);
/* intel supports a way to hide the upper leaves of cpuid, beyond 3.
* the bios might have done this, so we'll make sure it is off. */
if (cpu_has_feat(CPU_FEAT_X86_VENDOR_INTEL)) {
msr_val = read_msr(IA32_MISC_ENABLE);
if (msr_val & (1 << 22))
write_msr(IA32_MISC_ENABLE, msr_val & ~(1 << 22));
}
cprintf("Largest Standard Function Number Supported: %d\n", eax);
max_std_lvl = eax;
cpuid(0x80000000, 0x0, &eax, 0, 0, 0);
cprintf("Largest Extended Function Number Supported: 0x%08x\n", eax);
max_extd_lvl = eax;
cpuid(1, 0x0, &eax, &ebx, &ecx, &edx);
ext_family = (eax >> 20) & 0xff;
ext_model = (eax >> 16) & 0xf;
family = (eax >> 8) & 0xf;
model = (eax >> 4) & 0xf;
if ((family == 15) || (family == 6))
model += ext_model << 4;
if (family == 15)
family += ext_family;
x86_family = family;
x86_model = model;
x86_stepping = eax & 0xf;
printk("Family: %d\n", x86_family);
printk("Model: %d\n", x86_model);
printk("Stepping: %d\n", x86_stepping);
// eventually can fill this out with SDM Vol3B App B info, or
// better yet with stepping info. or cpuid 8000_000{2,3,4}
switch ( family << 8 | model ) {
case(0x061a):
cprintf("Processor: Core i7\n");
break;
case(0x060f):
cprintf("Processor: Core 2 Duo or Similar\n");
break;
default:
cprintf("Unknown or non-Intel CPU\n");
}
if (!(edx & 0x00000020))
panic("MSRs not supported!");
if (!(edx & 0x00001000))
panic("MTRRs not supported!");
if (!(edx & (1 << 16)))
panic("PAT not supported!");
if (!(edx & 0x00002000))
panic("Global Pages not supported!");
if (!(edx & 0x00000200))
panic("Local APIC Not Detected!");
if (ecx & 0x00200000)
cprintf("x2APIC Detected\n");
else
panic("x2APIC Not Detected\n");
/* Not sure how to detect AMD HW virt yet. */
if ((ecx & 0x00000060) && cpu_has_feat(CPU_FEAT_X86_VENDOR_INTEL)) {
msr_val = read_msr(IA32_FEATURE_CONTROL);
printd("64 Bit Feature Control: 0x%08x\n", msr_val);
if ((msr_val & 0x5) == 0x5)
printk("Hardware virtualization supported\n");
else
printk("Hardware virtualization not supported\n");
} else {
printk("Hardware virtualization not detected. (AMD?)\n");
}
/* FP and SSE Checks */
if (edx & 0x00000001)
printk("FPU Detected\n");
else
panic("FPU Not Detected!!\n");
printk("SSE support: ");
if (edx & (1 << 25))
printk("sse ");
else
panic("SSE Support Not Detected!!\n");
if (edx & (1 << 26))
printk("sse2 ");
if (ecx & (1 << 0))
printk("sse3 ");
if (ecx & (1 << 9))
printk("ssse3 ");
if (ecx & (1 << 19))
printk("sse4.1 ");
if (ecx & (1 << 20))
printk("sse4.2 ");
if (edx & (1 << 23))
printk("mmx ");
if ((edx & (1 << 25)) && (!(edx & (1 << 24))))
panic("SSE support, but no FXSAVE!");
printk("\n");
cpuid(0x80000008, 0x0, &eax, &ebx, &ecx, &edx);
cprintf("Physical Address Bits: %d\n", eax & 0x000000FF);
msr_val = read_msr(IA32_APIC_BASE);
if (!(msr_val & MSR_APIC_ENABLE))
panic("Local APIC Disabled!!");
cpuid(0x80000007, 0x0, &eax, &ebx, &ecx, &edx);
if (edx & 0x00000100)
printk("Invariant TSC present\n");
else
printk("Invariant TSC not present\n");
cpuid(0x07, 0x0, &eax, &ebx, &ecx, &edx);
if (ebx & 0x00000001) {
printk("FS/GS Base RD/W supported\n");
cpu_set_feat(CPU_FEAT_X86_FSGSBASE);
} else {
printk("FS/GS Base RD/W not supported\n");
#ifdef CONFIG_NOFASTCALL_FSBASE
panic("Can't write FS Base from userspace, and no FASTCALL support!");
#endif
}
cpuid(0x80000001, 0x0, &eax, &ebx, &ecx, &edx);
if (edx & (1 << 27)) {
printk("RDTSCP supported\n");
/* Set core 0's id, for use during boot (if FAST_COREID) */
write_msr(MSR_TSC_AUX, 0);
} else {
printk("RDTSCP not supported, but emulated for userspace\n");
}
/* Regardless, make sure userspace can access rdtsc (and rdtscp) */
lcr4(rcr4() & ~CR4_TSD);
printk("1 GB Jumbo pages %ssupported\n", edx & (1 << 26) ? "" : "not ");
if (!(edx & (1 << 29))) {
printk("Not 64 bit, refusing to boot!\n");
while (1)
asm volatile ("hlt");
}
cpuid(0x00000006, 0x0, &eax, 0, 0, 0);
if (eax & (1 << 2))
printk("Always running APIC detected\n");
else
printk("Always running APIC *not* detected\n");
/* TODO: Eventually consolidate all of our "cpuid" stuff. */
#define CPUID_FXSR_SUPPORT (1 << 24)
#define CPUID_XSAVE_SUPPORT (1 << 26)
#define CPUID_XSAVEOPT_SUPPORT (1 << 0)
#define CPUID_MONITOR_MWAIT (1 << 3)
#define CPUID_MWAIT_PWR_MGMT (1 << 0)
cpuid(0x01, 0x00, 0, 0, &ecx, &edx);
if (CPUID_FXSR_SUPPORT & edx)
cpu_set_feat(CPU_FEAT_X86_FXSR);
if (CPUID_XSAVE_SUPPORT & ecx)
cpu_set_feat(CPU_FEAT_X86_XSAVE);
cpuid(0x0d, 0x01, &eax, 0, 0, 0);
if (CPUID_XSAVEOPT_SUPPORT & eax)
cpu_set_feat(CPU_FEAT_X86_XSAVEOPT);
cpuid(0x01, 0x00, 0, 0, &ecx, 0);
if (CPUID_MONITOR_MWAIT & ecx) {
cpuid(0x05, 0x00, 0, 0, &ecx, 0);
if (CPUID_MWAIT_PWR_MGMT & ecx)
cpu_set_feat(CPU_FEAT_X86_MWAIT);
}
}
#define BIT_SPACING " "
#define BIT_DASHES "----------------"
void show_mapping(pgdir_t pgdir, uintptr_t start, size_t size)
{
pte_t pte;
int perm;
page_t *page;
uintptr_t i;
printk(" %sVirtual %sPhysical Ps Dr Ac G CD WT U W P EPTE\n",
BIT_SPACING, BIT_SPACING);
printk("-------------------------------------------------%s\n",
BIT_DASHES);
for(i = 0; i < size; i += PGSIZE, start += PGSIZE) {
pte = pgdir_walk(pgdir, (void*)start, 0);
printk("%p ", start);
if (pte_walk_okay(pte)) {
/* A note on PTE perms. If you look at just the PTE,
* you don't get the full picture for W and U. Those
* are the intersection of all bits. In Akaros, we do U
* or not at the earliest point (PML4 entries). All
* other PTEs have U set. For W, it's the opposite.
* The PTE for the actual page has W or not, and all
* others has W set. W needs to be more fine-grained,
* but U doesn't. Plus the UVPT mapping requires the U
* to see interior pages (but have W off). */
perm = get_va_perms(pgdir, (void*)start);
printk("%p %1d %1d %1d %1d %1d %1d %1d %1d %1d 0x%llx\n",
pte_get_paddr(pte),
pte_is_jumbo(pte),
pte_is_dirty(pte),
pte_is_accessed(pte),
(pte_print(pte) & PTE_G) / PTE_G,
(pte_print(pte) & __PTE_PCD) / __PTE_PCD,
(pte_print(pte) & __PTE_PWT) / __PTE_PWT,
(perm & PTE_U) / PTE_U,
(perm & PTE_W) / PTE_W,
pte_is_present(pte),
*(unsigned long*)kpte_to_epte(pte));
} else {
printk("%p\n", 0);
}
}
}
/* return 0 if ok, -1 if it failed for some reason.
* Be sensible and call it with 16 bytes.
*/
int vendor_id(char *vid)
{
uint32_t eax, ebx, ecx, edx;
asm volatile ("cpuid;"
"movl %%ebx, (%2);"
"movl %%edx, 4(%2);"
"movl %%ecx, 8(%2);"
: "=a"(eax)
: "a"(0), "D"(vid)
: "%ebx", "%ecx", "%edx");
vid[12] = '\0';
return 0;
}