f25f64ed5b
Due to index register access ordering problems, when using macros a line like this fails (and does nothing): setCx86(CX86_CCR2, getCx86(CX86_CCR2) | 0x88); With inlined functions this line will work as expected. Note about a side effect: Seems on Geode GX1 based systems the "suspend on halt power saving feature" was never enabled due to this wrong macro expansion. With inlined functions it will be enabled, but this will stop the TSC when the CPU runs into a HLT instruction. Kernel output something like this: Clocksource tsc unstable (delta = -472746897 ns) This is the 3rd version of this patch. - Adding missed arch/i386/kernel/cpu/mtrr/state.c Thanks to Andres Salomon - Adding some big fat comments into the new header file Suggested by Andi Kleen AK: fixed x86-64 compilation Signed-off-by: Juergen Beisert <juergen@kreuzholzen.de> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
381 lines
8.7 KiB
C
381 lines
8.7 KiB
C
#include <linux/init.h>
|
|
#include <linux/mm.h>
|
|
#include <asm/mtrr.h>
|
|
#include <asm/msr.h>
|
|
#include <asm/io.h>
|
|
#include <asm/processor-cyrix.h>
|
|
#include "mtrr.h"
|
|
|
|
int arr3_protected;
|
|
|
|
static void
|
|
cyrix_get_arr(unsigned int reg, unsigned long *base,
|
|
unsigned long *size, mtrr_type * type)
|
|
{
|
|
unsigned long flags;
|
|
unsigned char arr, ccr3, rcr, shift;
|
|
|
|
arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
|
|
|
|
/* Save flags and disable interrupts */
|
|
local_irq_save(flags);
|
|
|
|
ccr3 = getCx86(CX86_CCR3);
|
|
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
|
|
((unsigned char *) base)[3] = getCx86(arr);
|
|
((unsigned char *) base)[2] = getCx86(arr + 1);
|
|
((unsigned char *) base)[1] = getCx86(arr + 2);
|
|
rcr = getCx86(CX86_RCR_BASE + reg);
|
|
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
|
|
|
|
/* Enable interrupts if it was enabled previously */
|
|
local_irq_restore(flags);
|
|
shift = ((unsigned char *) base)[1] & 0x0f;
|
|
*base >>= PAGE_SHIFT;
|
|
|
|
/* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7
|
|
* Note: shift==0xf means 4G, this is unsupported.
|
|
*/
|
|
if (shift)
|
|
*size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1);
|
|
else
|
|
*size = 0;
|
|
|
|
/* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */
|
|
if (reg < 7) {
|
|
switch (rcr) {
|
|
case 1:
|
|
*type = MTRR_TYPE_UNCACHABLE;
|
|
break;
|
|
case 8:
|
|
*type = MTRR_TYPE_WRBACK;
|
|
break;
|
|
case 9:
|
|
*type = MTRR_TYPE_WRCOMB;
|
|
break;
|
|
case 24:
|
|
default:
|
|
*type = MTRR_TYPE_WRTHROUGH;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (rcr) {
|
|
case 0:
|
|
*type = MTRR_TYPE_UNCACHABLE;
|
|
break;
|
|
case 8:
|
|
*type = MTRR_TYPE_WRCOMB;
|
|
break;
|
|
case 9:
|
|
*type = MTRR_TYPE_WRBACK;
|
|
break;
|
|
case 25:
|
|
default:
|
|
*type = MTRR_TYPE_WRTHROUGH;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)
|
|
/* [SUMMARY] Get a free ARR.
|
|
<base> The starting (base) address of the region.
|
|
<size> The size (in bytes) of the region.
|
|
[RETURNS] The index of the region on success, else -1 on error.
|
|
*/
|
|
{
|
|
int i;
|
|
mtrr_type ltype;
|
|
unsigned long lbase, lsize;
|
|
|
|
switch (replace_reg) {
|
|
case 7:
|
|
if (size < 0x40)
|
|
break;
|
|
case 6:
|
|
case 5:
|
|
case 4:
|
|
return replace_reg;
|
|
case 3:
|
|
if (arr3_protected)
|
|
break;
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
return replace_reg;
|
|
}
|
|
/* If we are to set up a region >32M then look at ARR7 immediately */
|
|
if (size > 0x2000) {
|
|
cyrix_get_arr(7, &lbase, &lsize, <ype);
|
|
if (lsize == 0)
|
|
return 7;
|
|
/* Else try ARR0-ARR6 first */
|
|
} else {
|
|
for (i = 0; i < 7; i++) {
|
|
cyrix_get_arr(i, &lbase, &lsize, <ype);
|
|
if ((i == 3) && arr3_protected)
|
|
continue;
|
|
if (lsize == 0)
|
|
return i;
|
|
}
|
|
/* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */
|
|
cyrix_get_arr(i, &lbase, &lsize, <ype);
|
|
if ((lsize == 0) && (size >= 0x40))
|
|
return i;
|
|
}
|
|
return -ENOSPC;
|
|
}
|
|
|
|
static u32 cr4 = 0;
|
|
static u32 ccr3;
|
|
|
|
static void prepare_set(void)
|
|
{
|
|
u32 cr0;
|
|
|
|
/* Save value of CR4 and clear Page Global Enable (bit 7) */
|
|
if ( cpu_has_pge ) {
|
|
cr4 = read_cr4();
|
|
write_cr4(cr4 & ~X86_CR4_PGE);
|
|
}
|
|
|
|
/* Disable and flush caches. Note that wbinvd flushes the TLBs as
|
|
a side-effect */
|
|
cr0 = read_cr0() | 0x40000000;
|
|
wbinvd();
|
|
write_cr0(cr0);
|
|
wbinvd();
|
|
|
|
/* Cyrix ARRs - everything else were excluded at the top */
|
|
ccr3 = getCx86(CX86_CCR3);
|
|
|
|
/* Cyrix ARRs - everything else were excluded at the top */
|
|
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
|
|
|
|
}
|
|
|
|
static void post_set(void)
|
|
{
|
|
/* Flush caches and TLBs */
|
|
wbinvd();
|
|
|
|
/* Cyrix ARRs - everything else was excluded at the top */
|
|
setCx86(CX86_CCR3, ccr3);
|
|
|
|
/* Enable caches */
|
|
write_cr0(read_cr0() & 0xbfffffff);
|
|
|
|
/* Restore value of CR4 */
|
|
if ( cpu_has_pge )
|
|
write_cr4(cr4);
|
|
}
|
|
|
|
static void cyrix_set_arr(unsigned int reg, unsigned long base,
|
|
unsigned long size, mtrr_type type)
|
|
{
|
|
unsigned char arr, arr_type, arr_size;
|
|
|
|
arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
|
|
|
|
/* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */
|
|
if (reg >= 7)
|
|
size >>= 6;
|
|
|
|
size &= 0x7fff; /* make sure arr_size <= 14 */
|
|
for (arr_size = 0; size; arr_size++, size >>= 1) ;
|
|
|
|
if (reg < 7) {
|
|
switch (type) {
|
|
case MTRR_TYPE_UNCACHABLE:
|
|
arr_type = 1;
|
|
break;
|
|
case MTRR_TYPE_WRCOMB:
|
|
arr_type = 9;
|
|
break;
|
|
case MTRR_TYPE_WRTHROUGH:
|
|
arr_type = 24;
|
|
break;
|
|
default:
|
|
arr_type = 8;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (type) {
|
|
case MTRR_TYPE_UNCACHABLE:
|
|
arr_type = 0;
|
|
break;
|
|
case MTRR_TYPE_WRCOMB:
|
|
arr_type = 8;
|
|
break;
|
|
case MTRR_TYPE_WRTHROUGH:
|
|
arr_type = 25;
|
|
break;
|
|
default:
|
|
arr_type = 9;
|
|
break;
|
|
}
|
|
}
|
|
|
|
prepare_set();
|
|
|
|
base <<= PAGE_SHIFT;
|
|
setCx86(arr, ((unsigned char *) &base)[3]);
|
|
setCx86(arr + 1, ((unsigned char *) &base)[2]);
|
|
setCx86(arr + 2, (((unsigned char *) &base)[1]) | arr_size);
|
|
setCx86(CX86_RCR_BASE + reg, arr_type);
|
|
|
|
post_set();
|
|
}
|
|
|
|
typedef struct {
|
|
unsigned long base;
|
|
unsigned long size;
|
|
mtrr_type type;
|
|
} arr_state_t;
|
|
|
|
static arr_state_t arr_state[8] = {
|
|
{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL},
|
|
{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}
|
|
};
|
|
|
|
static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
static void cyrix_set_all(void)
|
|
{
|
|
int i;
|
|
|
|
prepare_set();
|
|
|
|
/* the CCRs are not contiguous */
|
|
for (i = 0; i < 4; i++)
|
|
setCx86(CX86_CCR0 + i, ccr_state[i]);
|
|
for (; i < 7; i++)
|
|
setCx86(CX86_CCR4 + i, ccr_state[i]);
|
|
for (i = 0; i < 8; i++)
|
|
cyrix_set_arr(i, arr_state[i].base,
|
|
arr_state[i].size, arr_state[i].type);
|
|
|
|
post_set();
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection
|
|
* with the SMM (System Management Mode) mode. So we need the following:
|
|
* Check whether SMI_LOCK (CCR3 bit 0) is set
|
|
* if it is set, write a warning message: ARR3 cannot be changed!
|
|
* (it cannot be changed until the next processor reset)
|
|
* if it is reset, then we can change it, set all the needed bits:
|
|
* - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset)
|
|
* - disable access to SMM memory (CCR1 bit 2 reset)
|
|
* - disable SMM mode (CCR1 bit 1 reset)
|
|
* - disable write protection of ARR3 (CCR6 bit 1 reset)
|
|
* - (maybe) disable ARR3
|
|
* Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set)
|
|
*/
|
|
static void __init
|
|
cyrix_arr_init(void)
|
|
{
|
|
struct set_mtrr_context ctxt;
|
|
unsigned char ccr[7];
|
|
int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
|
#ifdef CONFIG_SMP
|
|
int i;
|
|
#endif
|
|
|
|
/* flush cache and enable MAPEN */
|
|
set_mtrr_prepare_save(&ctxt);
|
|
set_mtrr_cache_disable(&ctxt);
|
|
|
|
/* Save all CCRs locally */
|
|
ccr[0] = getCx86(CX86_CCR0);
|
|
ccr[1] = getCx86(CX86_CCR1);
|
|
ccr[2] = getCx86(CX86_CCR2);
|
|
ccr[3] = ctxt.ccr3;
|
|
ccr[4] = getCx86(CX86_CCR4);
|
|
ccr[5] = getCx86(CX86_CCR5);
|
|
ccr[6] = getCx86(CX86_CCR6);
|
|
|
|
if (ccr[3] & 1) {
|
|
ccrc[3] = 1;
|
|
arr3_protected = 1;
|
|
} else {
|
|
/* Disable SMM mode (bit 1), access to SMM memory (bit 2) and
|
|
* access to SMM memory through ARR3 (bit 7).
|
|
*/
|
|
if (ccr[1] & 0x80) {
|
|
ccr[1] &= 0x7f;
|
|
ccrc[1] |= 0x80;
|
|
}
|
|
if (ccr[1] & 0x04) {
|
|
ccr[1] &= 0xfb;
|
|
ccrc[1] |= 0x04;
|
|
}
|
|
if (ccr[1] & 0x02) {
|
|
ccr[1] &= 0xfd;
|
|
ccrc[1] |= 0x02;
|
|
}
|
|
arr3_protected = 0;
|
|
if (ccr[6] & 0x02) {
|
|
ccr[6] &= 0xfd;
|
|
ccrc[6] = 1; /* Disable write protection of ARR3 */
|
|
setCx86(CX86_CCR6, ccr[6]);
|
|
}
|
|
/* Disable ARR3. This is safe now that we disabled SMM. */
|
|
/* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */
|
|
}
|
|
/* If we changed CCR1 in memory, change it in the processor, too. */
|
|
if (ccrc[1])
|
|
setCx86(CX86_CCR1, ccr[1]);
|
|
|
|
/* Enable ARR usage by the processor */
|
|
if (!(ccr[5] & 0x20)) {
|
|
ccr[5] |= 0x20;
|
|
ccrc[5] = 1;
|
|
setCx86(CX86_CCR5, ccr[5]);
|
|
}
|
|
#ifdef CONFIG_SMP
|
|
for (i = 0; i < 7; i++)
|
|
ccr_state[i] = ccr[i];
|
|
for (i = 0; i < 8; i++)
|
|
cyrix_get_arr(i,
|
|
&arr_state[i].base, &arr_state[i].size,
|
|
&arr_state[i].type);
|
|
#endif
|
|
|
|
set_mtrr_done(&ctxt); /* flush cache and disable MAPEN */
|
|
|
|
if (ccrc[5])
|
|
printk(KERN_INFO "mtrr: ARR usage was not enabled, enabled manually\n");
|
|
if (ccrc[3])
|
|
printk(KERN_INFO "mtrr: ARR3 cannot be changed\n");
|
|
/*
|
|
if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n");
|
|
if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n");
|
|
if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n");
|
|
*/
|
|
if (ccrc[6])
|
|
printk(KERN_INFO "mtrr: ARR3 was write protected, unprotected\n");
|
|
}
|
|
#endif
|
|
|
|
static struct mtrr_ops cyrix_mtrr_ops = {
|
|
.vendor = X86_VENDOR_CYRIX,
|
|
// .init = cyrix_arr_init,
|
|
.set_all = cyrix_set_all,
|
|
.set = cyrix_set_arr,
|
|
.get = cyrix_get_arr,
|
|
.get_free_region = cyrix_get_free_region,
|
|
.validate_add_page = generic_validate_add_page,
|
|
.have_wrcomb = positive_have_wrcomb,
|
|
};
|
|
|
|
int __init cyrix_init_mtrr(void)
|
|
{
|
|
set_mtrr_ops(&cyrix_mtrr_ops);
|
|
return 0;
|
|
}
|
|
|
|
//arch_initcall(cyrix_init_mtrr);
|