2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/init.h>
|
2009-07-04 10:22:08 +08:00
|
|
|
#include <linux/io.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/mm.h>
|
2009-07-04 10:22:08 +08:00
|
|
|
|
2007-07-22 17:12:38 +08:00
|
|
|
#include <asm/processor-cyrix.h>
|
2008-01-30 20:30:39 +08:00
|
|
|
#include <asm/processor-flags.h>
|
2009-07-04 10:22:08 +08:00
|
|
|
#include <asm/mtrr.h>
|
|
|
|
#include <asm/msr.h>
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "mtrr.h"
|
|
|
|
|
|
|
|
static void
|
|
|
|
cyrix_get_arr(unsigned int reg, unsigned long *base,
|
[PATCH] i386: fix MTRR code
Until not so long ago, there were system log messages pointing to
inconsistent MTRR setup of the video frame buffer caused by the way vesafb
and X worked. While vesafb was fixed meanwhile, I believe fixing it there
only hides a shortcoming in the MTRR code itself, in that that code is not
symmetric with respect to the ordering of attempts to set up two (or more)
regions where one contains the other. In the current shape, it permits
only setting up sub-regions of pre-exisiting ones. The patch below makes
this symmetric.
While working on that I noticed a few more inconsistencies in that code,
namely
- use of 'unsigned int' for sizes in many, but not all places (the patch
is converting this to use 'unsigned long' everywhere, which specifically
might be necessary for x86-64 once a processor supporting more than 44
physical address bits would become available)
- the code to correct inconsistent settings during secondary processor
startup tried (if necessary) to correct, among other things, the value
in IA32_MTRR_DEF_TYPE, however the newly computed value would never get
used (i.e. stored in the respective MSR)
- the generic range validation code checked that the end of the
to-be-added range would be above 1MB; the value checked should have been
the start of the range
- when contained regions are detected, previously this was allowed only
when the old region was uncacheable; this can be symmetric (i.e. the new
region can also be uncacheable) and even further as per Intel's
documentation write-trough and write-back for either region is also
compatible with the respective opposite in the other
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
2006-12-07 09:14:09 +08:00
|
|
|
unsigned long *size, mtrr_type * type)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned char arr, ccr3, rcr, shift;
|
2009-07-04 10:22:08 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
|
|
|
|
ccr3 = getCx86(CX86_CCR3);
|
|
|
|
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
|
2009-07-04 10:22:08 +08:00
|
|
|
((unsigned char *)base)[3] = getCx86(arr);
|
|
|
|
((unsigned char *)base)[2] = getCx86(arr + 1);
|
|
|
|
((unsigned char *)base)[1] = getCx86(arr + 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
rcr = getCx86(CX86_RCR_BASE + reg);
|
2009-07-04 10:22:08 +08:00
|
|
|
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
local_irq_restore(flags);
|
2009-07-04 10:22:08 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
shift = ((unsigned char *) base)[1] & 0x0f;
|
|
|
|
*base >>= PAGE_SHIFT;
|
|
|
|
|
2009-07-04 10:22:08 +08:00
|
|
|
/*
|
|
|
|
* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7
|
2005-04-17 06:20:36 +08:00
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-04 10:22:08 +08:00
|
|
|
/*
|
|
|
|
* cyrix_get_free_region - 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.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
static int
|
[PATCH] i386: fix MTRR code
Until not so long ago, there were system log messages pointing to
inconsistent MTRR setup of the video frame buffer caused by the way vesafb
and X worked. While vesafb was fixed meanwhile, I believe fixing it there
only hides a shortcoming in the MTRR code itself, in that that code is not
symmetric with respect to the ordering of attempts to set up two (or more)
regions where one contains the other. In the current shape, it permits
only setting up sub-regions of pre-exisiting ones. The patch below makes
this symmetric.
While working on that I noticed a few more inconsistencies in that code,
namely
- use of 'unsigned int' for sizes in many, but not all places (the patch
is converting this to use 'unsigned long' everywhere, which specifically
might be necessary for x86-64 once a processor supporting more than 44
physical address bits would become available)
- the code to correct inconsistent settings during secondary processor
startup tried (if necessary) to correct, among other things, the value
in IA32_MTRR_DEF_TYPE, however the newly computed value would never get
used (i.e. stored in the respective MSR)
- the generic range validation code checked that the end of the
to-be-added range would be above 1MB; the value checked should have been
the start of the range
- when contained regions are detected, previously this was allowed only
when the old region was uncacheable; this can be symmetric (i.e. the new
region can also be uncacheable) and even further as per Intel's
documentation write-trough and write-back for either region is also
compatible with the respective opposite in the other
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
2006-12-07 09:14:09 +08:00
|
|
|
cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
[PATCH] i386: fix MTRR code
Until not so long ago, there were system log messages pointing to
inconsistent MTRR setup of the video frame buffer caused by the way vesafb
and X worked. While vesafb was fixed meanwhile, I believe fixing it there
only hides a shortcoming in the MTRR code itself, in that that code is not
symmetric with respect to the ordering of attempts to set up two (or more)
regions where one contains the other. In the current shape, it permits
only setting up sub-regions of pre-exisiting ones. The patch below makes
this symmetric.
While working on that I noticed a few more inconsistencies in that code,
namely
- use of 'unsigned int' for sizes in many, but not all places (the patch
is converting this to use 'unsigned long' everywhere, which specifically
might be necessary for x86-64 once a processor supporting more than 44
physical address bits would become available)
- the code to correct inconsistent settings during secondary processor
startup tried (if necessary) to correct, among other things, the value
in IA32_MTRR_DEF_TYPE, however the newly computed value would never get
used (i.e. stored in the respective MSR)
- the generic range validation code checked that the end of the
to-be-added range would be above 1MB; the value checked should have been
the start of the range
- when contained regions are detected, previously this was allowed only
when the old region was uncacheable; this can be symmetric (i.e. the new
region can also be uncacheable) and even further as per Intel's
documentation write-trough and write-back for either region is also
compatible with the respective opposite in the other
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
2006-12-07 09:14:09 +08:00
|
|
|
unsigned long lbase, lsize;
|
2009-07-04 10:22:08 +08:00
|
|
|
mtrr_type ltype;
|
|
|
|
int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] i386: fix MTRR code
Until not so long ago, there were system log messages pointing to
inconsistent MTRR setup of the video frame buffer caused by the way vesafb
and X worked. While vesafb was fixed meanwhile, I believe fixing it there
only hides a shortcoming in the MTRR code itself, in that that code is not
symmetric with respect to the ordering of attempts to set up two (or more)
regions where one contains the other. In the current shape, it permits
only setting up sub-regions of pre-exisiting ones. The patch below makes
this symmetric.
While working on that I noticed a few more inconsistencies in that code,
namely
- use of 'unsigned int' for sizes in many, but not all places (the patch
is converting this to use 'unsigned long' everywhere, which specifically
might be necessary for x86-64 once a processor supporting more than 44
physical address bits would become available)
- the code to correct inconsistent settings during secondary processor
startup tried (if necessary) to correct, among other things, the value
in IA32_MTRR_DEF_TYPE, however the newly computed value would never get
used (i.e. stored in the respective MSR)
- the generic range validation code checked that the end of the
to-be-added range would be above 1MB; the value checked should have been
the start of the range
- when contained regions are detected, previously this was allowed only
when the old region was uncacheable; this can be symmetric (i.e. the new
region can also be uncacheable) and even further as per Intel's
documentation write-trough and write-back for either region is also
compatible with the respective opposite in the other
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Andi Kleen <ak@suse.de>
2006-12-07 09:14:09 +08:00
|
|
|
switch (replace_reg) {
|
|
|
|
case 7:
|
|
|
|
if (size < 0x40)
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
case 5:
|
|
|
|
case 4:
|
|
|
|
return replace_reg;
|
|
|
|
case 3:
|
|
|
|
case 2:
|
|
|
|
case 1:
|
|
|
|
case 0:
|
|
|
|
return replace_reg;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
/* 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;
|
2009-07-04 10:22:08 +08:00
|
|
|
/* Else try ARR0-ARR6 first */
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
|
|
cyrix_get_arr(i, &lbase, &lsize, <ype);
|
|
|
|
if (lsize == 0)
|
|
|
|
return i;
|
|
|
|
}
|
2009-07-04 10:22:08 +08:00
|
|
|
/*
|
|
|
|
* ARR0-ARR6 isn't free
|
|
|
|
* try ARR7 but its size must be at least 256K
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
cyrix_get_arr(i, &lbase, &lsize, <ype);
|
|
|
|
if ((lsize == 0) && (size >= 0x40))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
|
2009-07-04 10:22:08 +08:00
|
|
|
static u32 cr4, ccr3;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void prepare_set(void)
|
|
|
|
{
|
|
|
|
u32 cr0;
|
|
|
|
|
|
|
|
/* Save value of CR4 and clear Page Global Enable (bit 7) */
|
2009-07-04 10:22:08 +08:00
|
|
|
if (cpu_has_pge) {
|
2005-04-17 06:20:36 +08:00
|
|
|
cr4 = read_cr4();
|
2007-05-21 20:31:53 +08:00
|
|
|
write_cr4(cr4 & ~X86_CR4_PGE);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-07-04 10:22:08 +08:00
|
|
|
/*
|
|
|
|
* Disable and flush caches.
|
|
|
|
* Note that wbinvd flushes the TLBs as a side-effect
|
|
|
|
*/
|
2008-01-30 20:30:39 +08:00
|
|
|
cr0 = read_cr0() | X86_CR0_CD;
|
2005-04-17 06:20:36 +08:00
|
|
|
wbinvd();
|
|
|
|
write_cr0(cr0);
|
|
|
|
wbinvd();
|
|
|
|
|
2007-10-20 07:13:56 +08:00
|
|
|
/* Cyrix ARRs - everything else was excluded at the top */
|
2005-04-17 06:20:36 +08:00
|
|
|
ccr3 = getCx86(CX86_CCR3);
|
|
|
|
|
2007-10-20 07:13:56 +08:00
|
|
|
/* Cyrix ARRs - everything else was excluded at the top */
|
2005-04-17 06:20:36 +08:00
|
|
|
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void post_set(void)
|
|
|
|
{
|
2009-07-04 10:22:08 +08:00
|
|
|
/* Flush caches and TLBs */
|
2005-04-17 06:20:36 +08:00
|
|
|
wbinvd();
|
|
|
|
|
|
|
|
/* Cyrix ARRs - everything else was excluded at the top */
|
|
|
|
setCx86(CX86_CCR3, ccr3);
|
2009-07-04 10:22:08 +08:00
|
|
|
|
|
|
|
/* Enable caches */
|
2005-04-17 06:20:36 +08:00
|
|
|
write_cr0(read_cr0() & 0xbfffffff);
|
|
|
|
|
2009-07-04 10:22:08 +08:00
|
|
|
/* Restore value of CR4 */
|
|
|
|
if (cpu_has_pge)
|
2005-04-17 06:20:36 +08:00
|
|
|
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 */
|
2009-07-04 10:22:08 +08:00
|
|
|
for (arr_size = 0; size; arr_size++, size >>= 1)
|
|
|
|
;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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;
|
2009-07-04 10:22:08 +08:00
|
|
|
setCx86(arr + 0, ((unsigned char *)&base)[3]);
|
|
|
|
setCx86(arr + 1, ((unsigned char *)&base)[2]);
|
|
|
|
setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
setCx86(CX86_RCR_BASE + reg, arr_type);
|
|
|
|
|
|
|
|
post_set();
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
2009-07-04 10:22:08 +08:00
|
|
|
unsigned long base;
|
|
|
|
unsigned long size;
|
|
|
|
mtrr_type type;
|
2005-04-17 06:20:36 +08:00
|
|
|
} arr_state_t;
|
|
|
|
|
2007-06-28 05:09:49 +08:00
|
|
|
static arr_state_t arr_state[8] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL},
|
|
|
|
{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}
|
|
|
|
};
|
|
|
|
|
2007-06-28 05:09:49 +08:00
|
|
|
static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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]);
|
2009-07-04 10:22:08 +08:00
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
cyrix_set_arr(i, arr_state[i].base,
|
2005-04-17 06:20:36 +08:00
|
|
|
arr_state[i].size, arr_state[i].type);
|
2009-07-04 10:22:08 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
post_set();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct mtrr_ops cyrix_mtrr_ops = {
|
|
|
|
.vendor = X86_VENDOR_CYRIX,
|
|
|
|
.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;
|
|
|
|
}
|