Files
haiku-beta6/src/system/kernel/arch/x86/arch_cpu.c
T
Axel Dörfler 57b9fd2d91 Applied patch by Olivier Coursière: this should help rebooting on some
machines, and fixes bug #1884, thanks!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24202 a95241bf-73f2-0310-859d-f6bbb57e9c96
2008-03-02 13:51:17 +00:00

711 lines
18 KiB
C

/*
* Copyright 2002-2008, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <cpu.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <boot_device.h>
#include <commpage.h>
#include <smp.h>
#include <tls.h>
#include <vm.h>
#include <arch_system_info.h>
#include <arch/x86/selector.h>
#include <boot/kernel_args.h>
#include "interrupts.h"
/* cpu vendor info */
struct cpu_vendor_info {
const char *vendor;
const char *ident_string[2];
};
static const struct cpu_vendor_info vendor_info[VENDOR_NUM] = {
{ "Intel", { "GenuineIntel" } },
{ "AMD", { "AuthenticAMD" } },
{ "Cyrix", { "CyrixInstead" } },
{ "UMC", { "UMC UMC UMC" } },
{ "NexGen", { "NexGenDriven" } },
{ "Centaur", { "CentaurHauls" } },
{ "Rise", { "RiseRiseRise" } },
{ "Transmeta", { "GenuineTMx86", "TransmetaCPU" } },
{ "NSC", { "Geode by NSC" } },
};
#define CR0_CACHE_DISABLE (1UL << 30)
#define CR0_NOT_WRITE_THROUGH (1UL << 29)
#define CR0_FPU_EMULATION (1UL << 2)
#define CR0_MONITOR_FPU (1UL << 1)
#define CR4_OS_FXSR (1UL << 9)
#define CR4_OS_XMM_EXCEPTION (1UL << 10)
struct set_mtrr_parameter {
int32 index;
uint64 base;
uint64 length;
uint8 type;
};
extern void reboot(void);
// from arch_x86.S
void (*gX86SwapFPUFunc)(void *oldState, const void *newState);
bool gHasSSE = false;
static uint32 sCpuRendezvous;
static uint32 sCpuRendezvous2;
segment_descriptor *gGDT = NULL;
/* Some specials for the double fault handler */
//static struct tss sDoubleFaultTSS;
static uint32 sDoubleFaultStack[10240];
static x86_cpu_module_info *sCpuModule;
extern void memcpy_generic(void* dest, const void* source, size_t count);
extern int memcpy_generic_end;
x86_optimized_functions gOptimizedFunctions = {
.memcpy = memcpy_generic,
.memcpy_end = &memcpy_generic_end
};
/** Disable CPU caches, and invalidate them. */
static void
disable_caches()
{
x86_write_cr0((x86_read_cr0() | CR0_CACHE_DISABLE) & ~CR0_NOT_WRITE_THROUGH);
wbinvd();
arch_cpu_global_TLB_invalidate();
}
/** Invalidate CPU caches, and enable them. */
static void
enable_caches()
{
wbinvd();
arch_cpu_global_TLB_invalidate();
x86_write_cr0(x86_read_cr0() & ~(CR0_CACHE_DISABLE | CR0_NOT_WRITE_THROUGH));
}
static void
set_mtrr(void *_parameter, int cpu)
{
struct set_mtrr_parameter *parameter = (struct set_mtrr_parameter *)_parameter;
// wait until all CPUs have arrived here
smp_cpu_rendezvous(&sCpuRendezvous, cpu);
disable_caches();
sCpuModule->set_mtrr(parameter->index, parameter->base, parameter->length,
parameter->type);
enable_caches();
// wait until all CPUs have arrived here
smp_cpu_rendezvous(&sCpuRendezvous2, cpu);
}
static void
init_mtrrs(void *_unused, int cpu)
{
// wait until all CPUs have arrived here
smp_cpu_rendezvous(&sCpuRendezvous, cpu);
disable_caches();
sCpuModule->init_mtrrs();
enable_caches();
// wait until all CPUs have arrived here
smp_cpu_rendezvous(&sCpuRendezvous2, cpu);
}
uint32
x86_count_mtrrs(void)
{
if (sCpuModule == NULL)
return 0;
return sCpuModule->count_mtrrs();
}
void
x86_set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type)
{
cpu_status state;
struct set_mtrr_parameter parameter;
parameter.index = index;
parameter.base = base;
parameter.length = length;
parameter.type = type;
sCpuRendezvous = sCpuRendezvous2 = 0;
call_all_cpus(&set_mtrr, &parameter);
}
status_t
x86_get_mtrr(uint32 index, uint64 *_base, uint64 *_length, uint8 *_type)
{
// the MTRRs are identical on all CPUs, so it doesn't matter
// on which CPU this runs
return sCpuModule->get_mtrr(index, _base, _length, _type);
}
static void
init_sse(void)
{
if (!x86_check_feature(IA32_FEATURE_SSE, FEATURE_COMMON)
|| !x86_check_feature(IA32_FEATURE_FXSR, FEATURE_COMMON)) {
// we don't have proper SSE support
return;
}
// enable OS support for SSE
x86_write_cr4(x86_read_cr4() | CR4_OS_FXSR | CR4_OS_XMM_EXCEPTION);
x86_write_cr0(x86_read_cr0() & ~(CR0_FPU_EMULATION | CR0_MONITOR_FPU));
gX86SwapFPUFunc = i386_fxsave_swap;
gHasSSE = true;
}
static void
load_tss(int cpu)
{
short seg = ((TSS_BASE_SEGMENT + cpu) << 3) | DPL_KERNEL;
asm("movw %0, %%ax;"
"ltr %%ax;" : : "r" (seg) : "eax");
}
static void
init_double_fault(int cpuNum)
{
/* set up the double fault tss */
/* TODO: Axel - fix SMP support */
struct tss *tss = &gCPU[cpuNum].arch.double_fault_tss;
memset(tss, 0, sizeof(struct tss));
tss->sp0 = (uint32)sDoubleFaultStack + sizeof(sDoubleFaultStack);
tss->ss0 = KERNEL_DATA_SEG;
read_cr3(tss->cr3);
// copy the current cr3 to the double fault cr3
tss->eip = (uint32)&double_fault;
tss->es = KERNEL_DATA_SEG;
tss->cs = KERNEL_CODE_SEG;
tss->ss = KERNEL_DATA_SEG;
tss->esp = tss->sp0;
tss->ds = KERNEL_DATA_SEG;
tss->fs = KERNEL_DATA_SEG;
tss->gs = KERNEL_DATA_SEG;
tss->ldt_seg_selector = 0;
// add TSS descriptor for this new TSS
set_tss_descriptor(&gGDT[DOUBLE_FAULT_TSS_BASE_SEGMENT + cpuNum],
(addr_t)tss, sizeof(struct tss));
}
static void
make_feature_string(cpu_ent *cpu, char *str, size_t strlen)
{
str[0] = 0;
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_FPU)
strlcat(str, "fpu ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_VME)
strlcat(str, "vme ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_DE)
strlcat(str, "de ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PSE)
strlcat(str, "pse ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_TSC)
strlcat(str, "tsc ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_MSR)
strlcat(str, "msr ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PAE)
strlcat(str, "pae ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_MCE)
strlcat(str, "mce ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_CX8)
strlcat(str, "cx8 ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_APIC)
strlcat(str, "apic ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_SEP)
strlcat(str, "sep ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_MTRR)
strlcat(str, "mtrr ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PGE)
strlcat(str, "pge ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_MCA)
strlcat(str, "mca ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_CMOV)
strlcat(str, "cmov ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PAT)
strlcat(str, "pat ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PSE36)
strlcat(str, "pse36 ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PSN)
strlcat(str, "psn ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_CLFSH)
strlcat(str, "clfsh ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_DS)
strlcat(str, "ds ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_ACPI)
strlcat(str, "acpi ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_MMX)
strlcat(str, "mmx ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_FXSR)
strlcat(str, "fxsr ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_SSE)
strlcat(str, "sse ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_SSE2)
strlcat(str, "sse2 ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_SS)
strlcat(str, "ss ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_HTT)
strlcat(str, "htt ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_TM)
strlcat(str, "tm ", strlen);
if(cpu->arch.feature[FEATURE_COMMON] & IA32_FEATURE_PBE)
strlcat(str, "pbe ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_SSE3)
strlcat(str, "sse3 ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_MONITOR)
strlcat(str, "monitor ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_DSCPL)
strlcat(str, "dscpl ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_EST)
strlcat(str, "est ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_TM2)
strlcat(str, "tm2 ", strlen);
if(cpu->arch.feature[FEATURE_EXT] & IA32_FEATURE_EXT_CNXTID)
strlcat(str, "cnxtid ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_SYSCALL)
strlcat(str, "syscall ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_NX)
strlcat(str, "nx ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_MMXEXT)
strlcat(str, "mmxext ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_FFXSR)
strlcat(str, "ffxsr ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_LONG)
strlcat(str, "long ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_3DNOWEXT)
strlcat(str, "3dnowext ", strlen);
if(cpu->arch.feature[FEATURE_EXT_AMD] & IA32_FEATURE_AMD_EXT_3DNOW)
strlcat(str, "3dnow ", strlen);
}
static int
detect_cpu(int curr_cpu)
{
cpuid_info cpuid;
unsigned int data[4];
char vendor_str[17];
int i;
cpu_ent *cpu = get_cpu_struct();
// clear out the cpu info data
cpu->arch.vendor = VENDOR_UNKNOWN;
cpu->arch.vendor_name = "UNKNOWN VENDOR";
cpu->arch.feature[FEATURE_COMMON] = 0;
cpu->arch.feature[FEATURE_EXT] = 0;
cpu->arch.feature[FEATURE_EXT_AMD] = 0;
cpu->arch.model_name[0] = 0;
// print some fun data
get_current_cpuid(&cpuid, 0);
// build the vendor string
memset(vendor_str, 0, sizeof(vendor_str));
memcpy(vendor_str, cpuid.eax_0.vendor_id, sizeof(cpuid.eax_0.vendor_id));
// get the family, model, stepping
get_current_cpuid(&cpuid, 1);
cpu->arch.type = cpuid.eax_1.type;
cpu->arch.family = cpuid.eax_1.family;
cpu->arch.model = cpuid.eax_1.model;
cpu->arch.stepping = cpuid.eax_1.stepping;
dprintf("CPU %d: type %d family %d model %d stepping %d, string '%s'\n",
curr_cpu, cpu->arch.type, cpu->arch.family, cpu->arch.model, cpu->arch.stepping, vendor_str);
// figure out what vendor we have here
for(i=0; i<VENDOR_NUM; i++) {
if(vendor_info[i].ident_string[0] && !strcmp(vendor_str, vendor_info[i].ident_string[0])) {
cpu->arch.vendor = i;
cpu->arch.vendor_name = vendor_info[i].vendor;
break;
}
if(vendor_info[i].ident_string[1] && !strcmp(vendor_str, vendor_info[i].ident_string[1])) {
cpu->arch.vendor = i;
cpu->arch.vendor_name = vendor_info[i].vendor;
break;
}
}
// see if we can get the model name
get_current_cpuid(&cpuid, 0x80000000);
if(cpuid.eax_0.max_eax >= 0x80000004) {
// build the model string (need to swap ecx/edx data before copying)
unsigned int temp;
memset(cpu->arch.model_name, 0, sizeof(cpu->arch.model_name));
get_current_cpuid(&cpuid, 0x80000002);
temp = cpuid.regs.edx; cpuid.regs.edx = cpuid.regs.ecx; cpuid.regs.ecx = temp;
memcpy(cpu->arch.model_name, cpuid.as_chars, sizeof(cpuid.as_chars));
get_current_cpuid(&cpuid, 0x80000003);
temp = cpuid.regs.edx; cpuid.regs.edx = cpuid.regs.ecx; cpuid.regs.ecx = temp;
memcpy(cpu->arch.model_name + 16, cpuid.as_chars, sizeof(cpuid.as_chars));
get_current_cpuid(&cpuid, 0x80000004);
temp = cpuid.regs.edx; cpuid.regs.edx = cpuid.regs.ecx; cpuid.regs.ecx = temp;
memcpy(cpu->arch.model_name + 32, cpuid.as_chars, sizeof(cpuid.as_chars));
// some cpus return a right-justified string
for(i = 0; cpu->arch.model_name[i] == ' '; i++)
;
if(i > 0) {
memmove(cpu->arch.model_name,
&cpu->arch.model_name[i],
strlen(&cpu->arch.model_name[i]) + 1);
}
dprintf("CPU %d: vendor '%s' model name '%s'\n",
curr_cpu, cpu->arch.vendor_name, cpu->arch.model_name);
} else {
strcpy(cpu->arch.model_name, "unknown");
}
// load feature bits
get_current_cpuid(&cpuid, 1);
cpu->arch.feature[FEATURE_COMMON] = cpuid.eax_1.features; // edx
cpu->arch.feature[FEATURE_EXT] = cpuid.eax_1.extended_features; // ecx
if(cpu->arch.vendor == VENDOR_AMD) {
get_current_cpuid(&cpuid, 0x80000001);
cpu->arch.feature[FEATURE_EXT_AMD] = cpuid.regs.edx; // edx
}
make_feature_string(cpu, cpu->arch.feature_string, sizeof(cpu->arch.feature_string));
dprintf("CPU %d: features: %s\n", curr_cpu, cpu->arch.feature_string);
return 0;
}
bool
x86_check_feature(uint32 feature, enum x86_feature_type type)
{
cpu_ent *cpu = get_cpu_struct();
#if 0
int i;
dprintf("x86_check_feature: feature 0x%x, type %d\n", feature, type);
for (i = 0; i < FEATURE_NUM; i++) {
dprintf("features %d: 0x%x\n", i, cpu->arch.feature[i]);
}
#endif
return (cpu->arch.feature[type] & feature) ? TRUE : FALSE;
}
// #pragma mark -
status_t
arch_cpu_preboot_init_percpu(kernel_args *args, int curr_cpu)
{
x86_write_cr0(x86_read_cr0() & ~(CR0_FPU_EMULATION | CR0_MONITOR_FPU));
gX86SwapFPUFunc = i386_fnsave_swap;
return B_OK;
}
status_t
arch_cpu_init_percpu(kernel_args *args, int curr_cpu)
{
detect_cpu(curr_cpu);
// load the TSS for this cpu
// note the main cpu gets initialized in arch_cpu_init_post_vm()
if (curr_cpu != 0)
load_tss(curr_cpu);
return 0;
}
status_t
arch_cpu_init(kernel_args *args)
{
__x86_setup_system_time(args->arch_args.system_time_cv_factor);
return B_OK;
}
status_t
arch_cpu_init_post_vm(kernel_args *args)
{
uint32 i;
// account for the segment descriptors
gGDT = (segment_descriptor *)args->arch_args.vir_gdt;
create_area("gdt", (void **)&gGDT, B_EXACT_ADDRESS, B_PAGE_SIZE,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
// currently taken out of the build, because it's not yet used (and assumes
// (a fixed number of used GDT entries)
//i386_selector_init(gGDT); // pass the new gdt
// setup task-state segments
for (i = 0; i < args->num_cpus; i++) {
// initialize the regular and double fault tss stored in the per-cpu
// structure
memset(&gCPU[i].arch.tss, 0, sizeof(struct tss));
gCPU[i].arch.tss.ss0 = KERNEL_DATA_SEG;
// add TSS descriptor for this new TSS
set_tss_descriptor(&gGDT[TSS_BASE_SEGMENT + i],
(addr_t)&gCPU[i].arch.tss, sizeof(struct tss));
// initialize the double fault tss
init_double_fault(i);
}
// set the current hardware task on cpu 0
load_tss(0);
x86_set_task_gate(8, DOUBLE_FAULT_TSS_BASE_SEGMENT << 3);
// setup TLS descriptors (one for every CPU)
for (i = 0; i < args->num_cpus; i++) {
set_segment_descriptor(&gGDT[TLS_BASE_SEGMENT + i], 0, TLS_SIZE,
DT_DATA_WRITEABLE, DPL_USER);
}
// setup SSE2/3 support
init_sse();
return B_OK;
}
status_t
arch_cpu_init_post_modules(kernel_args *args)
{
// initialize CPU module
void *cookie = open_module_list("cpu");
while (true) {
char name[B_FILE_NAME_LENGTH];
size_t nameLength = sizeof(name);
if (read_next_module_name(cookie, name, &nameLength) != B_OK
|| get_module(name, (module_info **)&sCpuModule) == B_OK)
break;
}
close_module_list(cookie);
// initialize MTRRs if available
if (x86_count_mtrrs() > 0) {
sCpuRendezvous = sCpuRendezvous2 = 0;
call_all_cpus(&init_mtrrs, NULL);
}
// get optimized functions from the CPU module
if (sCpuModule != NULL && sCpuModule->get_optimized_functions != NULL) {
x86_optimized_functions functions;
memset(&functions, 0, sizeof(functions));
sCpuModule->get_optimized_functions(&functions);
if (functions.memcpy != NULL) {
gOptimizedFunctions.memcpy = functions.memcpy;
gOptimizedFunctions.memcpy_end = functions.memcpy_end;
}
}
// put the optimized functions into the commpage
fill_commpage_entry(COMMPAGE_ENTRY_X86_MEMCPY, gOptimizedFunctions.memcpy,
(addr_t)gOptimizedFunctions.memcpy_end
- (addr_t)gOptimizedFunctions.memcpy);
return B_OK;
}
void
i386_set_tss_and_kstack(addr_t kstack)
{
get_cpu_struct()->arch.tss.sp0 = kstack;
}
void
arch_cpu_global_TLB_invalidate(void)
{
uint32 flags = x86_read_cr4();
if (flags & IA32_CR4_GLOBAL_PAGES) {
// disable and reenable the global pages to flush all TLBs regardless
// of the global page bit
x86_write_cr4(flags & ~IA32_CR4_GLOBAL_PAGES);
x86_write_cr4(flags | IA32_CR4_GLOBAL_PAGES);
} else
arch_cpu_user_TLB_invalidate();
}
void
arch_cpu_invalidate_TLB_range(addr_t start, addr_t end)
{
int32 num_pages = end / B_PAGE_SIZE - start / B_PAGE_SIZE;
while (num_pages-- >= 0) {
invalidate_TLB(start);
start += B_PAGE_SIZE;
}
}
void
arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages)
{
int i;
for (i = 0; i < num_pages; i++) {
invalidate_TLB(pages[i]);
}
}
ssize_t
arch_cpu_user_strlcpy(char *to, const char *from, size_t size, addr_t *faultHandler)
{
int fromLength = 0;
addr_t oldFaultHandler = *faultHandler;
// this check is to trick the gcc4 compiler and have it keep the error label
if (to == NULL && size > 0)
goto error;
*faultHandler = (addr_t)&&error;
if (size > 0) {
to[--size] = '\0';
// copy
for ( ; size; size--, fromLength++, to++, from++) {
if ((*to = *from) == '\0')
break;
}
}
// count any leftover from chars
while (*from++ != '\0') {
fromLength++;
}
*faultHandler = oldFaultHandler;
return fromLength;
error:
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
status_t
arch_cpu_user_memset(void *s, char c, size_t count, addr_t *faultHandler)
{
char *xs = (char *)s;
addr_t oldFaultHandler = *faultHandler;
// this check is to trick the gcc4 compiler and have it keep the error label
if (s == NULL)
goto error;
*faultHandler = (addr_t)&&error;
while (count--)
*xs++ = c;
*faultHandler = oldFaultHandler;
return 0;
error:
*faultHandler = oldFaultHandler;
return B_BAD_ADDRESS;
}
status_t
arch_cpu_shutdown(bool rebootSystem)
{
cpu_status state = disable_interrupts();
if (!rebootSystem) {
status_t status = apm_shutdown();
restore_interrupts(state);
return status;
}
// try to reset the system using the keyboard controller
out8(0xfe, 0x64);
// Give some time to the controller to do its job (0.5s)
snooze(500000);
// if that didn't help, try it this way
reboot();
restore_interrupts(state);
return B_ERROR;
}
void
arch_cpu_idle(void)
{
asm("hlt");
}
void
arch_cpu_sync_icache(void *address, size_t length)
{
// instruction cache is always consistent on x86
}