* Copyright 2005-2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel DΓΆrfler, axeld@pinc-software.de
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#include "generic_x86.h"
#include "intel.h"
#include "amd.h"
#include "via.h"
#include <KernelExport.h>
#include <arch_system_info.h>
#include <arch/x86/arch_cpu.h>
#include <smp.h>
#ifdef TRACE_MTRR
# define TRACE(x...) dprintf("mtrr: " x)
#else
# define TRACE(x...)
#endif
#define IA32_MTRR_ENABLE (1UL << 11)
#define IA32_MTRR_ENABLE_FIXED (1UL << 10)
#define IA32_MTRR_VALID_RANGE (1UL << 11)
struct mtrr_capabilities {
mtrr_capabilities(uint64 value) { *(uint64 *)this = value; }
uint64 variable_ranges : 8;
uint64 supports_fixed : 1;
uint64 _reserved0 : 1;
uint64 supports_write_combined : 1;
uint64 _reserved1 : 53;
};
uint64 gPhysicalMask = 0;
static const char *
mtrr_type_to_string(uint8 type)
{
switch (type) {
case IA32_MTR_UNCACHED:
return "uncacheable";
case IA32_MTR_WRITE_COMBINING:
return "write combining";
case IA32_MTR_WRITE_THROUGH:
return "write-through";
case IA32_MTR_WRITE_PROTECTED:
return "write-protected";
case IA32_MTR_WRITE_BACK:
return "write-back";
default:
return "reserved";
}
}
static void
set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type)
{
uint64 mask = length - 1;
mask = ~mask & gPhysicalMask;
TRACE("MTRR %" B_PRIu32 ": new mask %" B_PRIx64 "\n", index, mask);
TRACE(" mask test base: %" B_PRIx64 "\n", mask & base);
TRACE(" mask test middle: %" B_PRIx64 "\n", mask & (base + length / 2));
TRACE(" mask test end: %" B_PRIx64 "\n", mask & (base + length));
index *= 2;
x86_write_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index, 0);
if (base != 0 || mask != 0 || type != 0) {
x86_write_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index,
(base & ~(B_PAGE_SIZE - 1)) | type);
x86_write_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index,
mask | IA32_MTRR_VALID_RANGE);
} else {
x86_write_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index, 0);
}
}
uint32
generic_count_mtrrs(void)
{
if (!x86_check_feature(IA32_FEATURE_MTRR, FEATURE_COMMON)
|| !x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON))
return 0;
mtrr_capabilities capabilities(x86_read_msr(IA32_MSR_MTRR_CAPABILITIES));
TRACE("CPU %" B_PRId32 " has %u variable range MTRRs.\n",
smp_get_current_cpu(), (uint8)capabilities.variable_ranges);
return capabilities.variable_ranges;
}
void
generic_init_mtrrs(uint32 count)
{
if (count == 0)
return;
TRACE("generic_init_mtrrs(count = %" B_PRIu32 ")\n", count);
uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE);
if ((defaultType & IA32_MTRR_ENABLE) == 0) {
for (uint32 i = 0; i < count; i++)
set_mtrr(i, 0, 0, 0);
}
x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE,
(defaultType & ~0xff) | IA32_MTRR_ENABLE);
}
void
generic_set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type)
{
set_mtrr(index, base, length, type);
TRACE("[cpu %" B_PRId32 "] mtrrs now:\n", smp_get_current_cpu());
#if TRACE_MTRR
generic_dump_mtrrs(generic_count_mtrrs());
#endif
}
status_t
generic_get_mtrr(uint32 index, uint64 *_base, uint64 *_length, uint8 *_type)
{
uint64 mask = x86_read_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index * 2);
if ((mask & IA32_MTRR_VALID_RANGE) == 0)
return B_ERROR;
uint64 base = x86_read_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index * 2);
*_base = base & ~(B_PAGE_SIZE - 1);
*_length = (~mask & gPhysicalMask) + B_PAGE_SIZE;
*_type = base & 0xff;
return B_OK;
}
void
generic_set_mtrrs(uint8 newDefaultType, const x86_mtrr_info* infos,
uint32 count, uint32 maxCount)
{
if (maxCount == 0)
return;
if (count > maxCount)
count = maxCount;
uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE)
& ~IA32_MTRR_ENABLE;
x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE, defaultType);
for (uint32 i = 0; i < count; i++)
set_mtrr(i, infos[i].base, infos[i].size, infos[i].type);
for (uint32 i = count; i < maxCount; i++)
set_mtrr(i, 0, 0, 0);
defaultType = (defaultType & ~(uint64)0xff) | newDefaultType;
x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE, defaultType | IA32_MTRR_ENABLE);
}
status_t
generic_mtrr_compute_physical_mask(void)
{
uint32 bits = 36;
cpuid_info cpuInfo;
if (get_current_cpuid(&cpuInfo, 0x80000000, 0) == B_OK
&& (cpuInfo.eax_0.max_eax & 0xff) >= 8) {
get_current_cpuid(&cpuInfo, 0x80000008, 0);
bits = cpuInfo.regs.eax & 0xff;
if (bits < 36)
bits = 36;
}
gPhysicalMask = ((1ULL << bits) - 1) & ~(B_PAGE_SIZE - 1);
TRACE("CPU %" B_PRId32 " has %" B_PRIu32
" physical address bits, physical mask is %016" B_PRIx64 "\n",
smp_get_current_cpu(), bits, gPhysicalMask);
return B_OK;
}
void
generic_dump_mtrrs(uint32 count)
{
if (count == 0)
return;
int cpu = smp_get_current_cpu();
uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE);
dprintf("mtrr: [cpu %d] MTRRs are %sabled\n", cpu,
(defaultType & IA32_MTRR_ENABLE) != 0 ? "en" : "dis");
dprintf("mtrr: [cpu %d] default type is %u %s\n", cpu,
(uint8)defaultType, mtrr_type_to_string(defaultType));
dprintf("mtrr: [cpu %d] fixed range MTRRs are %sabled\n", cpu,
(defaultType & IA32_MTRR_ENABLE_FIXED) != 0 ? "en" : "dis");
for (uint32 i = 0; i < count; i++) {
uint64 base;
uint64 length;
uint8 type;
if (generic_get_mtrr(i, &base, &length, &type) == B_OK) {
dprintf("mtrr: [cpu %d] %" B_PRIu32 ": base: 0x%" B_PRIx64
"; length: 0x%" B_PRIx64 "; type: %u %s\n", cpu, i, base,
length, type, mtrr_type_to_string(type));
}
}
}
module_info *modules[] = {
(module_info *)&gIntelModule,
(module_info *)&gAMDModule,
(module_info *)&gVIAModule,
NULL
};