Commit 3bb045f1 authored by H. Peter Anvin's avatar H. Peter Anvin

Merge branch 'x86/pat' into x86/urgent

Merge reason:

Suresh Siddha (1):
      x86, pat: don't use rb-tree based lookup in reserve_memtype()

... requires previous x86/pat commits already pushed to Linus.
Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
parents 80938332 dcb73bf4
...@@ -112,6 +112,10 @@ config IA64_UNCACHED_ALLOCATOR ...@@ -112,6 +112,10 @@ config IA64_UNCACHED_ALLOCATOR
bool bool
select GENERIC_ALLOCATOR select GENERIC_ALLOCATOR
config ARCH_USES_PG_UNCACHED
def_bool y
depends on IA64_UNCACHED_ALLOCATOR
config AUDIT_ARCH config AUDIT_ARCH
bool bool
default y default y
......
...@@ -1414,6 +1414,10 @@ config X86_PAT ...@@ -1414,6 +1414,10 @@ config X86_PAT
If unsure, say Y. If unsure, say Y.
config ARCH_USES_PG_UNCACHED
def_bool y
depends on X86_PAT
config EFI config EFI
bool "EFI runtime service support" bool "EFI runtime service support"
depends on ACPI depends on ACPI
......
...@@ -43,8 +43,58 @@ static inline void copy_from_user_page(struct vm_area_struct *vma, ...@@ -43,8 +43,58 @@ static inline void copy_from_user_page(struct vm_area_struct *vma,
memcpy(dst, src, len); memcpy(dst, src, len);
} }
#define PG_non_WB PG_arch_1 #define PG_WC PG_arch_1
PAGEFLAG(NonWB, non_WB) PAGEFLAG(WC, WC)
#ifdef CONFIG_X86_PAT
/*
* X86 PAT uses page flags WC and Uncached together to keep track of
* memory type of pages that have backing page struct. X86 PAT supports 3
* different memory types, _PAGE_CACHE_WB, _PAGE_CACHE_WC and
* _PAGE_CACHE_UC_MINUS and fourth state where page's memory type has not
* been changed from its default (value of -1 used to denote this).
* Note we do not support _PAGE_CACHE_UC here.
*
* Caller must hold memtype_lock for atomicity.
*/
static inline unsigned long get_page_memtype(struct page *pg)
{
if (!PageUncached(pg) && !PageWC(pg))
return -1;
else if (!PageUncached(pg) && PageWC(pg))
return _PAGE_CACHE_WC;
else if (PageUncached(pg) && !PageWC(pg))
return _PAGE_CACHE_UC_MINUS;
else
return _PAGE_CACHE_WB;
}
static inline void set_page_memtype(struct page *pg, unsigned long memtype)
{
switch (memtype) {
case _PAGE_CACHE_WC:
ClearPageUncached(pg);
SetPageWC(pg);
break;
case _PAGE_CACHE_UC_MINUS:
SetPageUncached(pg);
ClearPageWC(pg);
break;
case _PAGE_CACHE_WB:
SetPageUncached(pg);
SetPageWC(pg);
break;
default:
case -1:
ClearPageUncached(pg);
ClearPageWC(pg);
break;
}
}
#else
static inline unsigned long get_page_memtype(struct page *pg) { return -1; }
static inline void set_page_memtype(struct page *pg, unsigned long memtype) { }
#endif
/* /*
* The set_memory_* API can be used to change various attributes of a virtual * The set_memory_* API can be used to change various attributes of a virtual
......
...@@ -26,13 +26,16 @@ ...@@ -26,13 +26,16 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
int
is_io_mapping_possible(resource_size_t base, unsigned long size);
void * void *
iomap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot); iomap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot);
void void
iounmap_atomic(void *kvaddr, enum km_type type); iounmap_atomic(void *kvaddr, enum km_type type);
int
iomap_create_wc(resource_size_t base, unsigned long size, pgprot_t *prot);
void
iomap_free(resource_size_t base, unsigned long size);
#endif /* _ASM_X86_IOMAP_H */ #endif /* _ASM_X86_IOMAP_H */
...@@ -121,6 +121,9 @@ extern int mtrr_del_page(int reg, unsigned long base, unsigned long size); ...@@ -121,6 +121,9 @@ extern int mtrr_del_page(int reg, unsigned long base, unsigned long size);
extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi); extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi);
extern void mtrr_ap_init(void); extern void mtrr_ap_init(void);
extern void mtrr_bp_init(void); extern void mtrr_bp_init(void);
extern void set_mtrr_aps_delayed_init(void);
extern void mtrr_aps_init(void);
extern void mtrr_bp_restore(void);
extern int mtrr_trim_uncached_memory(unsigned long end_pfn); extern int mtrr_trim_uncached_memory(unsigned long end_pfn);
extern int amd_special_default_mtrr(void); extern int amd_special_default_mtrr(void);
# else # else
...@@ -161,6 +164,9 @@ static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) ...@@ -161,6 +164,9 @@ static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
#define mtrr_ap_init() do {} while (0) #define mtrr_ap_init() do {} while (0)
#define mtrr_bp_init() do {} while (0) #define mtrr_bp_init() do {} while (0)
#define set_mtrr_aps_delayed_init() do {} while (0)
#define mtrr_aps_init() do {} while (0)
#define mtrr_bp_restore() do {} while (0)
# endif # endif
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
......
...@@ -19,4 +19,9 @@ extern int free_memtype(u64 start, u64 end); ...@@ -19,4 +19,9 @@ extern int free_memtype(u64 start, u64 end);
extern int kernel_map_sync_memtype(u64 base, unsigned long size, extern int kernel_map_sync_memtype(u64 base, unsigned long size,
unsigned long flag); unsigned long flag);
int io_reserve_memtype(resource_size_t start, resource_size_t end,
unsigned long *type);
void io_free_memtype(resource_size_t start, resource_size_t end);
#endif /* _ASM_X86_PAT_H */ #endif /* _ASM_X86_PAT_H */
...@@ -153,7 +153,7 @@ int safe_smp_processor_id(void) ...@@ -153,7 +153,7 @@ int safe_smp_processor_id(void)
{ {
int apicid, cpuid; int apicid, cpuid;
if (!boot_cpu_has(X86_FEATURE_APIC)) if (!cpu_has_apic)
return 0; return 0;
apicid = hard_smp_processor_id(); apicid = hard_smp_processor_id();
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/io.h> #include <linux/io.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/cpu.h> #include <asm/cpu.h>
...@@ -45,8 +45,8 @@ static void __cpuinit init_amd_k5(struct cpuinfo_x86 *c) ...@@ -45,8 +45,8 @@ static void __cpuinit init_amd_k5(struct cpuinfo_x86 *c)
#define CBAR_ENB (0x80000000) #define CBAR_ENB (0x80000000)
#define CBAR_KEY (0X000000CB) #define CBAR_KEY (0X000000CB)
if (c->x86_model == 9 || c->x86_model == 10) { if (c->x86_model == 9 || c->x86_model == 10) {
if (inl (CBAR) & CBAR_ENB) if (inl(CBAR) & CBAR_ENB)
outl (0 | CBAR_KEY, CBAR); outl(0 | CBAR_KEY, CBAR);
} }
} }
...@@ -87,9 +87,10 @@ static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c) ...@@ -87,9 +87,10 @@ static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c)
d = d2-d; d = d2-d;
if (d > 20*K6_BUG_LOOP) if (d > 20*K6_BUG_LOOP)
printk("system stability may be impaired when more than 32 MB are used.\n"); printk(KERN_CONT
"system stability may be impaired when more than 32 MB are used.\n");
else else
printk("probably OK (after B9730xxxx).\n"); printk(KERN_CONT "probably OK (after B9730xxxx).\n");
printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n"); printk(KERN_INFO "Please see http://membres.lycos.fr/poulot/k6bug.html\n");
} }
...@@ -219,8 +220,9 @@ static void __cpuinit init_amd_k7(struct cpuinfo_x86 *c) ...@@ -219,8 +220,9 @@ static void __cpuinit init_amd_k7(struct cpuinfo_x86 *c)
if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) { if ((c->x86_model == 8 && c->x86_mask >= 1) || (c->x86_model > 8)) {
rdmsr(MSR_K7_CLK_CTL, l, h); rdmsr(MSR_K7_CLK_CTL, l, h);
if ((l & 0xfff00000) != 0x20000000) { if ((l & 0xfff00000) != 0x20000000) {
printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l, printk(KERN_INFO
((l & 0x000fffff)|0x20000000)); "CPU: CLK_CTL MSR was %x. Reprogramming to %x\n",
l, ((l & 0x000fffff)|0x20000000));
wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h); wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h);
} }
} }
...@@ -398,7 +400,7 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) ...@@ -398,7 +400,7 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
u32 level; u32 level;
level = cpuid_eax(1); level = cpuid_eax(1);
if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58) if ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
set_cpu_cap(c, X86_FEATURE_REP_GOOD); set_cpu_cap(c, X86_FEATURE_REP_GOOD);
/* /*
...@@ -507,14 +509,17 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) ...@@ -507,14 +509,17 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
} }
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c, unsigned int size) static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c,
unsigned int size)
{ {
/* AMD errata T13 (order #21922) */ /* AMD errata T13 (order #21922) */
if ((c->x86 == 6)) { if ((c->x86 == 6)) {
if (c->x86_model == 3 && c->x86_mask == 0) /* Duron Rev A0 */ /* Duron Rev A0 */
if (c->x86_model == 3 && c->x86_mask == 0)
size = 64; size = 64;
/* Tbird rev A1/A2 */
if (c->x86_model == 4 && if (c->x86_model == 4 &&
(c->x86_mask == 0 || c->x86_mask == 1)) /* Tbird rev A1/A2 */ (c->x86_mask == 0 || c->x86_mask == 1))
size = 256; size = 256;
} }
return size; return size;
......
...@@ -81,7 +81,7 @@ static void __init check_fpu(void) ...@@ -81,7 +81,7 @@ static void __init check_fpu(void)
boot_cpu_data.fdiv_bug = fdiv_bug; boot_cpu_data.fdiv_bug = fdiv_bug;
if (boot_cpu_data.fdiv_bug) if (boot_cpu_data.fdiv_bug)
printk("Hmm, FPU with FDIV bug.\n"); printk(KERN_WARNING "Hmm, FPU with FDIV bug.\n");
} }
static void __init check_hlt(void) static void __init check_hlt(void)
...@@ -98,7 +98,7 @@ static void __init check_hlt(void) ...@@ -98,7 +98,7 @@ static void __init check_hlt(void)
halt(); halt();
halt(); halt();
halt(); halt();
printk("OK.\n"); printk(KERN_CONT "OK.\n");
} }
/* /*
...@@ -122,9 +122,9 @@ static void __init check_popad(void) ...@@ -122,9 +122,9 @@ static void __init check_popad(void)
* CPU hard. Too bad. * CPU hard. Too bad.
*/ */
if (res != 12345678) if (res != 12345678)
printk("Buggy.\n"); printk(KERN_CONT "Buggy.\n");
else else
printk("OK.\n"); printk(KERN_CONT "OK.\n");
#endif #endif
} }
...@@ -156,7 +156,7 @@ void __init check_bugs(void) ...@@ -156,7 +156,7 @@ void __init check_bugs(void)
{ {
identify_boot_cpu(); identify_boot_cpu();
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
printk("CPU: "); printk(KERN_INFO "CPU: ");
print_cpu_info(&boot_cpu_data); print_cpu_info(&boot_cpu_data);
#endif #endif
check_config(); check_config();
......
...@@ -15,7 +15,7 @@ void __init check_bugs(void) ...@@ -15,7 +15,7 @@ void __init check_bugs(void)
{ {
identify_boot_cpu(); identify_boot_cpu();
#if !defined(CONFIG_SMP) #if !defined(CONFIG_SMP)
printk("CPU: "); printk(KERN_INFO "CPU: ");
print_cpu_info(&boot_cpu_data); print_cpu_info(&boot_cpu_data);
#endif #endif
alternative_instructions(); alternative_instructions();
......
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
#include <asm/hypervisor.h> #include <asm/hypervisor.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/topology.h> #include <linux/topology.h>
#include <asm/cpumask.h> #include <linux/cpumask.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <asm/proto.h> #include <asm/proto.h>
...@@ -28,13 +28,13 @@ ...@@ -28,13 +28,13 @@
#include <asm/desc.h> #include <asm/desc.h>
#include <asm/i387.h> #include <asm/i387.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/numa.h> #include <linux/numa.h>
#include <asm/asm.h> #include <asm/asm.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/mce.h> #include <asm/mce.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/pat.h> #include <asm/pat.h>
#include <asm/smp.h> #include <linux/smp.h>
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
#include <asm/uv/uv.h> #include <asm/uv/uv.h>
...@@ -982,7 +982,7 @@ static __init int setup_disablecpuid(char *arg) ...@@ -982,7 +982,7 @@ static __init int setup_disablecpuid(char *arg)
__setup("clearcpuid=", setup_disablecpuid); __setup("clearcpuid=", setup_disablecpuid);
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table }; struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table };
DEFINE_PER_CPU_FIRST(union irq_stack_union, DEFINE_PER_CPU_FIRST(union irq_stack_union,
irq_stack_union) __aligned(PAGE_SIZE); irq_stack_union) __aligned(PAGE_SIZE);
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/io.h> #include <linux/io.h>
#include <asm/processor-cyrix.h> #include <asm/processor-cyrix.h>
#include <asm/processor-flags.h> #include <asm/processor-flags.h>
#include <asm/timer.h> #include <linux/timer.h>
#include <asm/pci-direct.h> #include <asm/pci-direct.h>
#include <asm/tsc.h> #include <asm/tsc.h>
...@@ -282,7 +282,8 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) ...@@ -282,7 +282,8 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
* The 5510/5520 companion chips have a funky PIT. * The 5510/5520 companion chips have a funky PIT.
*/ */
if (vendor == PCI_VENDOR_ID_CYRIX && if (vendor == PCI_VENDOR_ID_CYRIX &&
(device == PCI_DEVICE_ID_CYRIX_5510 || device == PCI_DEVICE_ID_CYRIX_5520)) (device == PCI_DEVICE_ID_CYRIX_5510 ||
device == PCI_DEVICE_ID_CYRIX_5520))
mark_tsc_unstable("cyrix 5510/5520 detected"); mark_tsc_unstable("cyrix 5510/5520 detected");
} }
#endif #endif
...@@ -299,7 +300,8 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c) ...@@ -299,7 +300,8 @@ static void __cpuinit init_cyrix(struct cpuinfo_x86 *c)
* ? : 0x7x * ? : 0x7x
* GX1 : 0x8x GX1 datasheet 56 * GX1 : 0x8x GX1 datasheet 56
*/ */
if ((0x30 <= dir1 && dir1 <= 0x6f) || (0x80 <= dir1 && dir1 <= 0x8f)) if ((0x30 <= dir1 && dir1 <= 0x6f) ||
(0x80 <= dir1 && dir1 <= 0x8f))
geode_configure(); geode_configure();
return; return;
} else { /* MediaGX */ } else { /* MediaGX */
...@@ -427,9 +429,12 @@ static void __cpuinit cyrix_identify(struct cpuinfo_x86 *c) ...@@ -427,9 +429,12 @@ static void __cpuinit cyrix_identify(struct cpuinfo_x86 *c)
printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n"); printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n");
local_irq_save(flags); local_irq_save(flags);
ccr3 = getCx86(CX86_CCR3); ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ /* enable MAPEN */
setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x80); /* enable cpuid */ setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ /* enable cpuid */
setCx86_old(CX86_CCR4, getCx86_old(CX86_CCR4) | 0x80);
/* disable MAPEN */
setCx86(CX86_CCR3, ccr3);
local_irq_restore(flags); local_irq_restore(flags);
} }
} }
......
...@@ -28,11 +28,10 @@ ...@@ -28,11 +28,10 @@
static inline void __cpuinit static inline void __cpuinit
detect_hypervisor_vendor(struct cpuinfo_x86 *c) detect_hypervisor_vendor(struct cpuinfo_x86 *c)
{ {
if (vmware_platform()) { if (vmware_platform())
c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE; c->x86_hyper_vendor = X86_HYPER_VENDOR_VMWARE;
} else { else
c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE; c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
}
} }
unsigned long get_hypervisor_tsc_freq(void) unsigned long get_hypervisor_tsc_freq(void)
......
...@@ -7,17 +7,17 @@ ...@@ -7,17 +7,17 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/uaccess.h>
#include <asm/ds.h> #include <asm/ds.h>
#include <asm/bugs.h> #include <asm/bugs.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
#include <asm/topology.h> #include <linux/topology.h>
#include <asm/numa_64.h> #include <asm/numa_64.h>
#endif #endif
...@@ -174,7 +174,8 @@ static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c) ...@@ -174,7 +174,8 @@ static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c)
#ifdef CONFIG_X86_F00F_BUG #ifdef CONFIG_X86_F00F_BUG
/* /*
* All current models of Pentium and Pentium with MMX technology CPUs * All current models of Pentium and Pentium with MMX technology CPUs
* have the F0 0F bug, which lets nonprivileged users lock up the system. * have the F0 0F bug, which lets nonprivileged users lock up the
* system.
* Note that the workaround only should be initialized once... * Note that the workaround only should be initialized once...
*/ */
c->f00f_bug = 0; c->f00f_bug = 0;
...@@ -207,7 +208,7 @@ static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c) ...@@ -207,7 +208,7 @@ static void __cpuinit intel_workarounds(struct cpuinfo_x86 *c)
printk (KERN_INFO "CPU: C0 stepping P4 Xeon detected.\n"); printk (KERN_INFO "CPU: C0 stepping P4 Xeon detected.\n");
printk (KERN_INFO "CPU: Disabling hardware prefetching (Errata 037)\n"); printk (KERN_INFO "CPU: Disabling hardware prefetching (Errata 037)\n");
lo |= MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE; lo |= MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE;
wrmsr (MSR_IA32_MISC_ENABLE, lo, hi); wrmsr(MSR_IA32_MISC_ENABLE, lo, hi);
} }
} }
...@@ -283,7 +284,7 @@ static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c) ...@@ -283,7 +284,7 @@ static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
/* Intel has a non-standard dependency on %ecx for this CPUID level. */ /* Intel has a non-standard dependency on %ecx for this CPUID level. */
cpuid_count(4, 0, &eax, &ebx, &ecx, &edx); cpuid_count(4, 0, &eax, &ebx, &ecx, &edx);
if (eax & 0x1f) if (eax & 0x1f)
return ((eax >> 26) + 1); return (eax >> 26) + 1;
else else
return 1; return 1;
} }
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/smp.h> #include <linux/smp.h>
#include <asm/k8.h> #include <asm/k8.h>
#define LVL_1_INST 1 #define LVL_1_INST 1
...@@ -25,14 +25,15 @@ ...@@ -25,14 +25,15 @@
#define LVL_3 4 #define LVL_3 4
#define LVL_TRACE 5 #define LVL_TRACE 5
struct _cache_table struct _cache_table {
{
unsigned char descriptor; unsigned char descriptor;
char cache_type; char cache_type;
short size; short size;
}; };
/* all the cache descriptor types we care about (no TLB or trace cache entries) */ /* All the cache descriptor types we care about (no TLB or
trace cache entries) */
static const struct _cache_table __cpuinitconst cache_table[] = static const struct _cache_table __cpuinitconst cache_table[] =
{ {
{ 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */ { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */
...@@ -105,8 +106,7 @@ static const struct _cache_table __cpuinitconst cache_table[] = ...@@ -105,8 +106,7 @@ static const struct _cache_table __cpuinitconst cache_table[] =
}; };
enum _cache_type enum _cache_type {
{
CACHE_TYPE_NULL = 0, CACHE_TYPE_NULL = 0,
CACHE_TYPE_DATA = 1, CACHE_TYPE_DATA = 1,
CACHE_TYPE_INST = 2, CACHE_TYPE_INST = 2,
...@@ -170,31 +170,31 @@ unsigned short num_cache_leaves; ...@@ -170,31 +170,31 @@ unsigned short num_cache_leaves;
Maybe later */ Maybe later */
union l1_cache { union l1_cache {
struct { struct {
unsigned line_size : 8; unsigned line_size:8;
unsigned lines_per_tag : 8; unsigned lines_per_tag:8;
unsigned assoc : 8; unsigned assoc:8;
unsigned size_in_kb : 8; unsigned size_in_kb:8;
}; };
unsigned val; unsigned val;
}; };
union l2_cache { union l2_cache {
struct { struct {
unsigned line_size : 8; unsigned line_size:8;
unsigned lines_per_tag : 4; unsigned lines_per_tag:4;
unsigned assoc : 4; unsigned assoc:4;
unsigned size_in_kb : 16; unsigned size_in_kb:16;
}; };
unsigned val; unsigned val;
}; };
union l3_cache { union l3_cache {
struct { struct {
unsigned line_size : 8; unsigned line_size:8;
unsigned lines_per_tag : 4; unsigned lines_per_tag:4;
unsigned assoc : 4; unsigned assoc:4;
unsigned res : 2; unsigned res:2;
unsigned size_encoded : 14; unsigned size_encoded:14;
}; };
unsigned val; unsigned val;
}; };
...@@ -350,7 +350,8 @@ static int __cpuinit find_num_cache_leaves(void) ...@@ -350,7 +350,8 @@ static int __cpuinit find_num_cache_leaves(void)
unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
{ {
unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0; /* Cache sizes */ /* Cache sizes */
unsigned int trace = 0, l1i = 0, l1d = 0, l2 = 0, l3 = 0;
unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */ unsigned int new_l1d = 0, new_l1i = 0; /* Cache sizes from cpuid(4) */
unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */ unsigned int new_l2 = 0, new_l3 = 0, i; /* Cache sizes from cpuid(4) */
unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb; unsigned int l2_id = 0, l3_id = 0, num_threads_sharing, index_msb;
...@@ -377,7 +378,7 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) ...@@ -377,7 +378,7 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
retval = cpuid4_cache_lookup_regs(i, &this_leaf); retval = cpuid4_cache_lookup_regs(i, &this_leaf);
if (retval >= 0) { if (retval >= 0) {
switch(this_leaf.eax.split.level) { switch (this_leaf.eax.split.level) {
case 1: case 1:
if (this_leaf.eax.split.type == if (this_leaf.eax.split.type ==
CACHE_TYPE_DATA) CACHE_TYPE_DATA)
...@@ -395,7 +396,8 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) ...@@ -395,7 +396,8 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
case 3: case 3:
new_l3 = this_leaf.size/1024; new_l3 = this_leaf.size/1024;
num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing; num_threads_sharing = 1 + this_leaf.eax.split.num_threads_sharing;
index_msb = get_count_order(num_threads_sharing); index_msb = get_count_order(
num_threads_sharing);
l3_id = c->apicid >> index_msb; l3_id = c->apicid >> index_msb;
break; break;
default: default:
...@@ -421,22 +423,21 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) ...@@ -421,22 +423,21 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
/* Number of times to iterate */ /* Number of times to iterate */
n = cpuid_eax(2) & 0xFF; n = cpuid_eax(2) & 0xFF;
for ( i = 0 ; i < n ; i++ ) { for (i = 0 ; i < n ; i++) {
cpuid(2, &regs[0], &regs[1], &regs[2], &regs[3]); cpuid(2, &regs[0], &regs[1], &regs[2], &regs[3]);
/* If bit 31 is set, this is an unknown format */ /* If bit 31 is set, this is an unknown format */
for ( j = 0 ; j < 3 ; j++ ) { for (j = 0 ; j < 3 ; j++)
if (regs[j] & (1 << 31)) regs[j] = 0; if (regs[j] & (1 << 31))
} regs[j] = 0;
/* Byte 0 is level count, not a descriptor */ /* Byte 0 is level count, not a descriptor */
for ( j = 1 ; j < 16 ; j++ ) { for (j = 1 ; j < 16 ; j++) {
unsigned char des = dp[j]; unsigned char des = dp[j];
unsigned char k = 0; unsigned char k = 0;
/* look up this descriptor in the table */ /* look up this descriptor in the table */
while (cache_table[k].descriptor != 0) while (cache_table[k].descriptor != 0) {
{
if (cache_table[k].descriptor == des) { if (cache_table[k].descriptor == des) {
if (only_trace && cache_table[k].cache_type != LVL_TRACE) if (only_trace && cache_table[k].cache_type != LVL_TRACE)
break; break;
...@@ -488,14 +489,14 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c) ...@@ -488,14 +489,14 @@ unsigned int __cpuinit init_intel_cacheinfo(struct cpuinfo_x86 *c)
} }
if (trace) if (trace)
printk (KERN_INFO "CPU: Trace cache: %dK uops", trace); printk(KERN_INFO "CPU: Trace cache: %dK uops", trace);
else if ( l1i ) else if (l1i)
printk (KERN_INFO "CPU: L1 I cache: %dK", l1i); printk(KERN_INFO "CPU: L1 I cache: %dK", l1i);
if (l1d) if (l1d)
printk(", L1 D cache: %dK\n", l1d); printk(KERN_CONT ", L1 D cache: %dK\n", l1d);
else else
printk("\n"); printk(KERN_CONT "\n");
if (l2) if (l2)
printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); printk(KERN_INFO "CPU: L2 cache: %dK\n", l2);
...@@ -558,8 +559,13 @@ static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index) ...@@ -558,8 +559,13 @@ static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index)
} }
} }
#else #else
static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index) {} static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index)
static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index) {} {
}
static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index)
{
}
#endif #endif
static void __cpuinit free_cache_attributes(unsigned int cpu) static void __cpuinit free_cache_attributes(unsigned int cpu)
...@@ -645,7 +651,7 @@ static DEFINE_PER_CPU(struct _index_kobject *, index_kobject); ...@@ -645,7 +651,7 @@ static DEFINE_PER_CPU(struct _index_kobject *, index_kobject);
static ssize_t show_##file_name \ static ssize_t show_##file_name \
(struct _cpuid4_info *this_leaf, char *buf) \ (struct _cpuid4_info *this_leaf, char *buf) \
{ \ { \
return sprintf (buf, "%lu\n", (unsigned long)this_leaf->object + val); \ return sprintf(buf, "%lu\n", (unsigned long)this_leaf->object + val); \
} }
show_one_plus(level, eax.split.level, 0); show_one_plus(level, eax.split.level, 0);
...@@ -656,7 +662,7 @@ show_one_plus(number_of_sets, ecx.split.number_of_sets, 1); ...@@ -656,7 +662,7 @@ show_one_plus(number_of_sets, ecx.split.number_of_sets, 1);
static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf) static ssize_t show_size(struct _cpuid4_info *this_leaf, char *buf)
{ {
return sprintf (buf, "%luK\n", this_leaf->size / 1024); return sprintf(buf, "%luK\n", this_leaf->size / 1024);
} }
static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf, static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf,
...@@ -669,7 +675,7 @@ static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf, ...@@ -669,7 +675,7 @@ static ssize_t show_shared_cpu_map_func(struct _cpuid4_info *this_leaf,
const struct cpumask *mask; const struct cpumask *mask;
mask = to_cpumask(this_leaf->shared_cpu_map); mask = to_cpumask(this_leaf->shared_cpu_map);
n = type? n = type ?
cpulist_scnprintf(buf, len-2, mask) : cpulist_scnprintf(buf, len-2, mask) :
cpumask_scnprintf(buf, len-2, mask); cpumask_scnprintf(buf, len-2, mask);
buf[n++] = '\n'; buf[n++] = '\n';
...@@ -800,7 +806,7 @@ static struct _cache_attr cache_disable_0 = __ATTR(cache_disable_0, 0644, ...@@ -800,7 +806,7 @@ static struct _cache_attr cache_disable_0 = __ATTR(cache_disable_0, 0644,
static struct _cache_attr cache_disable_1 = __ATTR(cache_disable_1, 0644, static struct _cache_attr cache_disable_1 = __ATTR(cache_disable_1, 0644,
show_cache_disable_1, store_cache_disable_1); show_cache_disable_1, store_cache_disable_1);
static struct attribute * default_attrs[] = { static struct attribute *default_attrs[] = {
&type.attr, &type.attr,
&level.attr, &level.attr,
&coherency_line_size.attr, &coherency_line_size.attr,
...@@ -815,7 +821,7 @@ static struct attribute * default_attrs[] = { ...@@ -815,7 +821,7 @@ static struct attribute * default_attrs[] = {
NULL NULL
}; };
static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf) static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{ {
struct _cache_attr *fattr = to_attr(attr); struct _cache_attr *fattr = to_attr(attr);
struct _index_kobject *this_leaf = to_object(kobj); struct _index_kobject *this_leaf = to_object(kobj);
...@@ -828,8 +834,8 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf) ...@@ -828,8 +834,8 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr, char * buf)
return ret; return ret;
} }
static ssize_t store(struct kobject * kobj, struct attribute * attr, static ssize_t store(struct kobject *kobj, struct attribute *attr,
const char * buf, size_t count) const char *buf, size_t count)
{ {
struct _cache_attr *fattr = to_attr(attr); struct _cache_attr *fattr = to_attr(attr);
struct _index_kobject *this_leaf = to_object(kobj); struct _index_kobject *this_leaf = to_object(kobj);
...@@ -883,7 +889,7 @@ static int __cpuinit cpuid4_cache_sysfs_init(unsigned int cpu) ...@@ -883,7 +889,7 @@ static int __cpuinit cpuid4_cache_sysfs_init(unsigned int cpu)
goto err_out; goto err_out;
per_cpu(index_kobject, cpu) = kzalloc( per_cpu(index_kobject, cpu) = kzalloc(
sizeof(struct _index_kobject ) * num_cache_leaves, GFP_KERNEL); sizeof(struct _index_kobject) * num_cache_leaves, GFP_KERNEL);
if (unlikely(per_cpu(index_kobject, cpu) == NULL)) if (unlikely(per_cpu(index_kobject, cpu) == NULL))
goto err_out; goto err_out;
...@@ -917,7 +923,7 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev) ...@@ -917,7 +923,7 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
} }
for (i = 0; i < num_cache_leaves; i++) { for (i = 0; i < num_cache_leaves; i++) {
this_object = INDEX_KOBJECT_PTR(cpu,i); this_object = INDEX_KOBJECT_PTR(cpu, i);
this_object->cpu = cpu; this_object->cpu = cpu;
this_object->index = i; this_object->index = i;
retval = kobject_init_and_add(&(this_object->kobj), retval = kobject_init_and_add(&(this_object->kobj),
...@@ -925,9 +931,8 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev) ...@@ -925,9 +931,8 @@ static int __cpuinit cache_add_dev(struct sys_device * sys_dev)
per_cpu(cache_kobject, cpu), per_cpu(cache_kobject, cpu),
"index%1lu", i); "index%1lu", i);
if (unlikely(retval)) { if (unlikely(retval)) {
for (j = 0; j < i; j++) { for (j = 0; j < i; j++)
kobject_put(&(INDEX_KOBJECT_PTR(cpu,j)->kobj)); kobject_put(&(INDEX_KOBJECT_PTR(cpu, j)->kobj));
}
kobject_put(per_cpu(cache_kobject, cpu)); kobject_put(per_cpu(cache_kobject, cpu));
cpuid4_cache_sysfs_exit(cpu); cpuid4_cache_sysfs_exit(cpu);
return retval; return retval;
...@@ -952,7 +957,7 @@ static void __cpuinit cache_remove_dev(struct sys_device * sys_dev) ...@@ -952,7 +957,7 @@ static void __cpuinit cache_remove_dev(struct sys_device * sys_dev)
cpumask_clear_cpu(cpu, to_cpumask(cache_dev_map)); cpumask_clear_cpu(cpu, to_cpumask(cache_dev_map));
for (i = 0; i < num_cache_leaves; i++) for (i = 0; i < num_cache_leaves; i++)
kobject_put(&(INDEX_KOBJECT_PTR(cpu,i)->kobj)); kobject_put(&(INDEX_KOBJECT_PTR(cpu, i)->kobj));
kobject_put(per_cpu(cache_kobject, cpu)); kobject_put(per_cpu(cache_kobject, cpu));
cpuid4_cache_sysfs_exit(cpu); cpuid4_cache_sysfs_exit(cpu);
} }
...@@ -977,8 +982,7 @@ static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb, ...@@ -977,8 +982,7 @@ static int __cpuinit cacheinfo_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK; return NOTIFY_OK;
} }
static struct notifier_block __cpuinitdata cacheinfo_cpu_notifier = static struct notifier_block __cpuinitdata cacheinfo_cpu_notifier = {
{
.notifier_call = cacheinfo_cpu_callback, .notifier_call = cacheinfo_cpu_callback,
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
static void static void
amd_get_mtrr(unsigned int reg, unsigned long *base, amd_get_mtrr(unsigned int reg, unsigned long *base,
unsigned long *size, mtrr_type * type) unsigned long *size, mtrr_type *type)
{ {
unsigned long low, high; unsigned long low, high;
...@@ -29,11 +29,11 @@ amd_get_mtrr(unsigned int reg, unsigned long *base, ...@@ -29,11 +29,11 @@ amd_get_mtrr(unsigned int reg, unsigned long *base,
/* /*
* This needs a little explaining. The size is stored as an * This needs a little explaining. The size is stored as an
* inverted mask of bits of 128K granularity 15 bits long offset * inverted mask of bits of 128K granularity 15 bits long offset
* 2 bits * 2 bits.
* *
* So to get a size we do invert the mask and add 1 to the lowest * So to get a size we do invert the mask and add 1 to the lowest
* mask bit (4 as its 2 bits in). This gives us a size we then shift * mask bit (4 as its 2 bits in). This gives us a size we then shift
* to turn into 128K blocks * to turn into 128K blocks.
* *
* eg 111 1111 1111 1100 is 512K * eg 111 1111 1111 1100 is 512K
* *
...@@ -43,40 +43,45 @@ amd_get_mtrr(unsigned int reg, unsigned long *base, ...@@ -43,40 +43,45 @@ amd_get_mtrr(unsigned int reg, unsigned long *base,
*/ */
low = (~low) & 0x1FFFC; low = (~low) & 0x1FFFC;
*size = (low + 4) << (15 - PAGE_SHIFT); *size = (low + 4) << (15 - PAGE_SHIFT);
return;
} }
static void amd_set_mtrr(unsigned int reg, unsigned long base, /**
unsigned long size, mtrr_type type) * amd_set_mtrr - Set variable MTRR register on the local CPU.
/* [SUMMARY] Set variable MTRR register on the local CPU. *
<reg> The register to set. * @reg The register to set.
<base> The base address of the region. * @base The base address of the region.
<size> The size of the region. If this is 0 the region is disabled. * @size The size of the region. If this is 0 the region is disabled.
<type> The type of the region. * @type The type of the region.
[RETURNS] Nothing. *
*/ * Returns nothing.
*/
static void
amd_set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)
{ {
u32 regs[2]; u32 regs[2];
/* /*
* Low is MTRR0 , High MTRR 1 * Low is MTRR0, High MTRR 1
*/ */
rdmsr(MSR_K6_UWCCR, regs[0], regs[1]); rdmsr(MSR_K6_UWCCR, regs[0], regs[1]);
/* /*
* Blank to disable * Blank to disable
*/ */
if (size == 0) if (size == 0) {
regs[reg] = 0; regs[reg] = 0;
else } else {
/* Set the register to the base, the type (off by one) and an /*
inverted bitmask of the size The size is the only odd * Set the register to the base, the type (off by one) and an
bit. We are fed say 512K We invert this and we get 111 1111 * inverted bitmask of the size The size is the only odd
1111 1011 but if you subtract one and invert you get the * bit. We are fed say 512K We invert this and we get 111 1111
desired 111 1111 1111 1100 mask * 1111 1011 but if you subtract one and invert you get the
* desired 111 1111 1111 1100 mask
But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! */ *
* But ~(x - 1) == ~x + 1 == -x. Two's complement rocks!
*/
regs[reg] = (-size >> (15 - PAGE_SHIFT) & 0x0001FFFC) regs[reg] = (-size >> (15 - PAGE_SHIFT) & 0x0001FFFC)
| (base << PAGE_SHIFT) | (type + 1); | (base << PAGE_SHIFT) | (type + 1);
}
/* /*
* The writeback rule is quite specific. See the manual. Its * The writeback rule is quite specific. See the manual. Its
...@@ -86,14 +91,16 @@ static void amd_set_mtrr(unsigned int reg, unsigned long base, ...@@ -86,14 +91,16 @@ static void amd_set_mtrr(unsigned int reg, unsigned long base,
wrmsr(MSR_K6_UWCCR, regs[0], regs[1]); wrmsr(MSR_K6_UWCCR, regs[0], regs[1]);
} }
static int amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type) static int
amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
{ {
/* Apply the K6 block alignment and size rules /*
In order * Apply the K6 block alignment and size rules
o Uncached or gathering only * In order
o 128K or bigger block * o Uncached or gathering only
o Power of 2 block * o 128K or bigger block
o base suitably aligned to the power * o Power of 2 block
* o base suitably aligned to the power
*/ */
if (type > MTRR_TYPE_WRCOMB || size < (1 << (17 - PAGE_SHIFT)) if (type > MTRR_TYPE_WRCOMB || size < (1 << (17 - PAGE_SHIFT))
|| (size & ~(size - 1)) - size || (base & (size - 1))) || (size & ~(size - 1)) - size || (base & (size - 1)))
...@@ -115,5 +122,3 @@ int __init amd_init_mtrr(void) ...@@ -115,5 +122,3 @@ int __init amd_init_mtrr(void)
set_mtrr_ops(&amd_mtrr_ops); set_mtrr_ops(&amd_mtrr_ops);
return 0; return 0;
} }
//arch_initcall(amd_mtrr_init);
#include <linux/init.h> #include <linux/init.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/msr.h> #include <asm/msr.h>
#include "mtrr.h" #include "mtrr.h"
static struct { static struct {
...@@ -12,25 +14,25 @@ static struct { ...@@ -12,25 +14,25 @@ static struct {
static u8 centaur_mcr_reserved; static u8 centaur_mcr_reserved;
static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */
/* /**
* Report boot time MCR setups * centaur_get_free_region - Get a free MTRR.
*
* @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.
*/ */
static int static int
centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg) centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg)
/* [SUMMARY] Get a free MTRR.
<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, max;
mtrr_type ltype;
unsigned long lbase, lsize; unsigned long lbase, lsize;
mtrr_type ltype;
int i, max;
max = num_var_ranges; max = num_var_ranges;
if (replace_reg >= 0 && replace_reg < max) if (replace_reg >= 0 && replace_reg < max)
return replace_reg; return replace_reg;
for (i = 0; i < max; ++i) { for (i = 0; i < max; ++i) {
if (centaur_mcr_reserved & (1 << i)) if (centaur_mcr_reserved & (1 << i))
continue; continue;
...@@ -38,11 +40,14 @@ centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg) ...@@ -38,11 +40,14 @@ centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg)
if (lsize == 0) if (lsize == 0)
return i; return i;
} }
return -ENOSPC; return -ENOSPC;
} }
void /*
mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) * Report boot time MCR setups
*/
void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
{ {
centaur_mcr[mcr].low = lo; centaur_mcr[mcr].low = lo;
centaur_mcr[mcr].high = hi; centaur_mcr[mcr].high = hi;
...@@ -54,17 +59,18 @@ centaur_get_mcr(unsigned int reg, unsigned long *base, ...@@ -54,17 +59,18 @@ centaur_get_mcr(unsigned int reg, unsigned long *base,
{ {
*base = centaur_mcr[reg].high >> PAGE_SHIFT; *base = centaur_mcr[reg].high >> PAGE_SHIFT;
*size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT;
*type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ *type = MTRR_TYPE_WRCOMB; /* write-combining */
if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2))
*type = MTRR_TYPE_UNCACHABLE; *type = MTRR_TYPE_UNCACHABLE;
if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25)
*type = MTRR_TYPE_WRBACK; *type = MTRR_TYPE_WRBACK;
if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31)
*type = MTRR_TYPE_WRBACK; *type = MTRR_TYPE_WRBACK;
} }
static void centaur_set_mcr(unsigned int reg, unsigned long base, static void
centaur_set_mcr(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type) unsigned long size, mtrr_type type)
{ {
unsigned long low, high; unsigned long low, high;
...@@ -74,13 +80,14 @@ static void centaur_set_mcr(unsigned int reg, unsigned long base, ...@@ -74,13 +80,14 @@ static void centaur_set_mcr(unsigned int reg, unsigned long base,
high = low = 0; high = low = 0;
} else { } else {
high = base << PAGE_SHIFT; high = base << PAGE_SHIFT;
if (centaur_mcr_type == 0) if (centaur_mcr_type == 0) {
low = -size << PAGE_SHIFT | 0x1f; /* only support write-combining... */ /* Only support write-combining... */
else { low = -size << PAGE_SHIFT | 0x1f;
} else {
if (type == MTRR_TYPE_UNCACHABLE) if (type == MTRR_TYPE_UNCACHABLE)
low = -size << PAGE_SHIFT | 0x02; /* NC */ low = -size << PAGE_SHIFT | 0x02; /* NC */
else else
low = -size << PAGE_SHIFT | 0x09; /* WWO,WC */ low = -size << PAGE_SHIFT | 0x09; /* WWO, WC */
} }
} }
centaur_mcr[reg].high = high; centaur_mcr[reg].high = high;
...@@ -88,118 +95,16 @@ static void centaur_set_mcr(unsigned int reg, unsigned long base, ...@@ -88,118 +95,16 @@ static void centaur_set_mcr(unsigned int reg, unsigned long base,
wrmsr(MSR_IDT_MCR0 + reg, low, high); wrmsr(MSR_IDT_MCR0 + reg, low, high);
} }
#if 0 static int
/* centaur_validate_add_page(unsigned long base, unsigned long size, unsigned int type)
* Initialise the later (saner) Winchip MCR variant. In this version
* the BIOS can pass us the registers it has used (but not their values)
* and the control register is read/write
*/
static void __init
centaur_mcr1_init(void)
{
unsigned i;
u32 lo, hi;
/* Unfortunately, MCR's are read-only, so there is no way to
* find out what the bios might have done.
*/
rdmsr(MSR_IDT_MCR_CTRL, lo, hi);
if (((lo >> 17) & 7) == 1) { /* Type 1 Winchip2 MCR */
lo &= ~0x1C0; /* clear key */
lo |= 0x040; /* set key to 1 */
wrmsr(MSR_IDT_MCR_CTRL, lo, hi); /* unlock MCR */
}
centaur_mcr_type = 1;
/*
* Clear any unconfigured MCR's.
*/
for (i = 0; i < 8; ++i) {
if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) {
if (!(lo & (1 << (9 + i))))
wrmsr(MSR_IDT_MCR0 + i, 0, 0);
else
/*
* If the BIOS set up an MCR we cannot see it
* but we don't wish to obliterate it
*/
centaur_mcr_reserved |= (1 << i);
}
}
/*
* Throw the main write-combining switch...
* However if OOSTORE is enabled then people have already done far
* cleverer things and we should behave.
*/
lo |= 15; /* Write combine enables */
wrmsr(MSR_IDT_MCR_CTRL, lo, hi);
}
/*
* Initialise the original winchip with read only MCR registers
* no used bitmask for the BIOS to pass on and write only control
*/
static void __init
centaur_mcr0_init(void)
{
unsigned i;
/* Unfortunately, MCR's are read-only, so there is no way to
* find out what the bios might have done.
*/
/* Clear any unconfigured MCR's.
* This way we are sure that the centaur_mcr array contains the actual
* values. The disadvantage is that any BIOS tweaks are thus undone.
*
*/
for (i = 0; i < 8; ++i) {
if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0)
wrmsr(MSR_IDT_MCR0 + i, 0, 0);
}
wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0); /* Write only */
}
/*
* Initialise Winchip series MCR registers
*/
static void __init
centaur_mcr_init(void)
{
struct set_mtrr_context ctxt;
set_mtrr_prepare_save(&ctxt);
set_mtrr_cache_disable(&ctxt);
if (boot_cpu_data.x86_model == 4)
centaur_mcr0_init();
else if (boot_cpu_data.x86_model == 8 || boot_cpu_data.x86_model == 9)
centaur_mcr1_init();
set_mtrr_done(&ctxt);
}
#endif
static int centaur_validate_add_page(unsigned long base,
unsigned long size, unsigned int type)
{ {
/* /*
* FIXME: Winchip2 supports uncached * FIXME: Winchip2 supports uncached
*/ */
if (type != MTRR_TYPE_WRCOMB && if (type != MTRR_TYPE_WRCOMB &&
(centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) {
printk(KERN_WARNING pr_warning("mtrr: only write-combining%s supported\n",
"mtrr: only write-combining%s supported\n", centaur_mcr_type ? " and uncacheable are" : " is");
centaur_mcr_type ? " and uncacheable are"
: " is");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
...@@ -207,7 +112,6 @@ static int centaur_validate_add_page(unsigned long base, ...@@ -207,7 +112,6 @@ static int centaur_validate_add_page(unsigned long base,
static struct mtrr_ops centaur_mtrr_ops = { static struct mtrr_ops centaur_mtrr_ops = {
.vendor = X86_VENDOR_CENTAUR, .vendor = X86_VENDOR_CENTAUR,
// .init = centaur_mcr_init,
.set = centaur_set_mcr, .set = centaur_set_mcr,
.get = centaur_get_mcr, .get = centaur_get_mcr,
.get_free_region = centaur_get_free_region, .get_free_region = centaur_get_free_region,
...@@ -220,5 +124,3 @@ int __init centaur_init_mtrr(void) ...@@ -220,5 +124,3 @@ int __init centaur_init_mtrr(void)
set_mtrr_ops(&centaur_mtrr_ops); set_mtrr_ops(&centaur_mtrr_ops);
return 0; return 0;
} }
//arch_initcall(centaur_init_mtrr);
/* MTRR (Memory Type Range Register) cleanup /*
* MTRR (Memory Type Range Register) cleanup
Copyright (C) 2009 Yinghai Lu *
* Copyright (C) 2009 Yinghai Lu
This library is free software; you can redistribute it and/or *
modify it under the terms of the GNU Library General Public * This library is free software; you can redistribute it and/or
License as published by the Free Software Foundation; either * modify it under the terms of the GNU Library General Public
version 2 of the License, or (at your option) any later version. * License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, *
but WITHOUT ANY WARRANTY; without even the implied warranty of * This library is distributed in the hope that it will be useful,
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * but WITHOUT ANY WARRANTY; without even the implied warranty of
Library General Public License for more details. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
You should have received a copy of the GNU Library General Public *
License along with this library; if not, write to the Free * You should have received a copy of the GNU Library General Public
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * License along with this library; if not, write to the Free
*/ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
#include <linux/kvm_para.h>
#include <asm/processor.h>
#include <asm/e820.h> #include <asm/e820.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/kvm_para.h>
#include "mtrr.h"
/* should be related to MTRR_VAR_RANGES nums */ #include "mtrr.h"
#define RANGE_NUM 256
struct res_range { struct res_range {
unsigned long start; unsigned long start;
unsigned long end; unsigned long end;
}; };
struct var_mtrr_range_state {
unsigned long base_pfn;
unsigned long size_pfn;
mtrr_type type;
};
struct var_mtrr_state {
unsigned long range_startk;
unsigned long range_sizek;
unsigned long chunk_sizek;
unsigned long gran_sizek;
unsigned int reg;
};
/* Should be related to MTRR_VAR_RANGES nums */
#define RANGE_NUM 256
static struct res_range __initdata range[RANGE_NUM];
static int __initdata nr_range;
static struct var_mtrr_range_state __initdata range_state[RANGE_NUM];
static int __initdata debug_print;
#define Dprintk(x...) do { if (debug_print) printk(KERN_DEBUG x); } while (0)
static int __init static int __init
add_range(struct res_range *range, int nr_range, unsigned long start, add_range(struct res_range *range, int nr_range,
unsigned long end) unsigned long start, unsigned long end)
{ {
/* out of slots */ /* Out of slots: */
if (nr_range >= RANGE_NUM) if (nr_range >= RANGE_NUM)
return nr_range; return nr_range;
...@@ -58,12 +82,12 @@ add_range(struct res_range *range, int nr_range, unsigned long start, ...@@ -58,12 +82,12 @@ add_range(struct res_range *range, int nr_range, unsigned long start,
} }
static int __init static int __init
add_range_with_merge(struct res_range *range, int nr_range, unsigned long start, add_range_with_merge(struct res_range *range, int nr_range,
unsigned long end) unsigned long start, unsigned long end)
{ {
int i; int i;
/* try to merge it with old one */ /* Try to merge it with old one: */
for (i = 0; i < nr_range; i++) { for (i = 0; i < nr_range; i++) {
unsigned long final_start, final_end; unsigned long final_start, final_end;
unsigned long common_start, common_end; unsigned long common_start, common_end;
...@@ -84,7 +108,7 @@ add_range_with_merge(struct res_range *range, int nr_range, unsigned long start, ...@@ -84,7 +108,7 @@ add_range_with_merge(struct res_range *range, int nr_range, unsigned long start,
return nr_range; return nr_range;
} }
/* need to add that */ /* Need to add it: */
return add_range(range, nr_range, start, end); return add_range(range, nr_range, start, end);
} }
...@@ -117,7 +141,7 @@ subtract_range(struct res_range *range, unsigned long start, unsigned long end) ...@@ -117,7 +141,7 @@ subtract_range(struct res_range *range, unsigned long start, unsigned long end)
} }
if (start > range[j].start && end < range[j].end) { if (start > range[j].start && end < range[j].end) {
/* find the new spare */ /* Find the new spare: */
for (i = 0; i < RANGE_NUM; i++) { for (i = 0; i < RANGE_NUM; i++) {
if (range[i].end == 0) if (range[i].end == 0)
break; break;
...@@ -146,14 +170,8 @@ static int __init cmp_range(const void *x1, const void *x2) ...@@ -146,14 +170,8 @@ static int __init cmp_range(const void *x1, const void *x2)
return start1 - start2; return start1 - start2;
} }
struct var_mtrr_range_state { #define BIOS_BUG_MSG KERN_WARNING \
unsigned long base_pfn; "WARNING: BIOS bug: VAR MTRR %d contains strange UC entry under 1M, check with your system vendor!\n"
unsigned long size_pfn;
mtrr_type type;
};
static struct var_mtrr_range_state __initdata range_state[RANGE_NUM];
static int __initdata debug_print;
static int __init static int __init
x86_get_mtrr_mem_range(struct res_range *range, int nr_range, x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
...@@ -180,7 +198,7 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range, ...@@ -180,7 +198,7 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
range[i].start, range[i].end + 1); range[i].start, range[i].end + 1);
} }
/* take out UC ranges */ /* Take out UC ranges: */
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
type = range_state[i].type; type = range_state[i].type;
if (type != MTRR_TYPE_UNCACHABLE && if (type != MTRR_TYPE_UNCACHABLE &&
...@@ -193,9 +211,7 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range, ...@@ -193,9 +211,7 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed && if (base < (1<<(20-PAGE_SHIFT)) && mtrr_state.have_fixed &&
(mtrr_state.enabled & 1)) { (mtrr_state.enabled & 1)) {
/* Var MTRR contains UC entry below 1M? Skip it: */ /* Var MTRR contains UC entry below 1M? Skip it: */
printk(KERN_WARNING "WARNING: BIOS bug: VAR MTRR %d " printk(BIOS_BUG_MSG, i);
"contains strange UC entry under 1M, check "
"with your system vendor!\n", i);
if (base + size <= (1<<(20-PAGE_SHIFT))) if (base + size <= (1<<(20-PAGE_SHIFT)))
continue; continue;
size -= (1<<(20-PAGE_SHIFT)) - base; size -= (1<<(20-PAGE_SHIFT)) - base;
...@@ -237,17 +253,13 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range, ...@@ -237,17 +253,13 @@ x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
return nr_range; return nr_range;
} }
static struct res_range __initdata range[RANGE_NUM];
static int __initdata nr_range;
#ifdef CONFIG_MTRR_SANITIZER #ifdef CONFIG_MTRR_SANITIZER
static unsigned long __init sum_ranges(struct res_range *range, int nr_range) static unsigned long __init sum_ranges(struct res_range *range, int nr_range)
{ {
unsigned long sum; unsigned long sum = 0;
int i; int i;
sum = 0;
for (i = 0; i < nr_range; i++) for (i = 0; i < nr_range; i++)
sum += range[i].end + 1 - range[i].start; sum += range[i].end + 1 - range[i].start;
...@@ -278,14 +290,6 @@ static int __init mtrr_cleanup_debug_setup(char *str) ...@@ -278,14 +290,6 @@ static int __init mtrr_cleanup_debug_setup(char *str)
} }
early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup); early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup);
struct var_mtrr_state {
unsigned long range_startk;
unsigned long range_sizek;
unsigned long chunk_sizek;
unsigned long gran_sizek;
unsigned int reg;
};
static void __init static void __init
set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
unsigned char type, unsigned int address_bits) unsigned char type, unsigned int address_bits)
...@@ -324,8 +328,7 @@ save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek, ...@@ -324,8 +328,7 @@ save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
range_state[reg].type = type; range_state[reg].type = type;
} }
static void __init static void __init set_var_mtrr_all(unsigned int address_bits)
set_var_mtrr_all(unsigned int address_bits)
{ {
unsigned long basek, sizek; unsigned long basek, sizek;
unsigned char type; unsigned char type;
...@@ -342,11 +345,11 @@ set_var_mtrr_all(unsigned int address_bits) ...@@ -342,11 +345,11 @@ set_var_mtrr_all(unsigned int address_bits)
static unsigned long to_size_factor(unsigned long sizek, char *factorp) static unsigned long to_size_factor(unsigned long sizek, char *factorp)
{ {
char factor;
unsigned long base = sizek; unsigned long base = sizek;
char factor;
if (base & ((1<<10) - 1)) { if (base & ((1<<10) - 1)) {
/* not MB alignment */ /* Not MB-aligned: */
factor = 'K'; factor = 'K';
} else if (base & ((1<<20) - 1)) { } else if (base & ((1<<20) - 1)) {
factor = 'M'; factor = 'M';
...@@ -372,11 +375,12 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk, ...@@ -372,11 +375,12 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk,
unsigned long max_align, align; unsigned long max_align, align;
unsigned long sizek; unsigned long sizek;
/* Compute the maximum size I can make a range */ /* Compute the maximum size with which we can make a range: */
if (range_startk) if (range_startk)
max_align = ffs(range_startk) - 1; max_align = ffs(range_startk) - 1;
else else
max_align = 32; max_align = 32;
align = fls(range_sizek) - 1; align = fls(range_sizek) - 1;
if (align > max_align) if (align > max_align)
align = max_align; align = max_align;
...@@ -386,11 +390,10 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk, ...@@ -386,11 +390,10 @@ range_to_mtrr(unsigned int reg, unsigned long range_startk,
char start_factor = 'K', size_factor = 'K'; char start_factor = 'K', size_factor = 'K';
unsigned long start_base, size_base; unsigned long start_base, size_base;
start_base = to_size_factor(range_startk, start_base = to_size_factor(range_startk, &start_factor);
&start_factor), size_base = to_size_factor(sizek, &size_factor);
size_base = to_size_factor(sizek, &size_factor),
printk(KERN_DEBUG "Setting variable MTRR %d, " Dprintk("Setting variable MTRR %d, "
"base: %ld%cB, range: %ld%cB, type %s\n", "base: %ld%cB, range: %ld%cB, type %s\n",
reg, start_base, start_factor, reg, start_base, start_factor,
size_base, size_factor, size_base, size_factor,
...@@ -425,10 +428,11 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -425,10 +428,11 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
chunk_sizek = state->chunk_sizek; chunk_sizek = state->chunk_sizek;
gran_sizek = state->gran_sizek; gran_sizek = state->gran_sizek;
/* align with gran size, prevent small block used up MTRRs */ /* Align with gran size, prevent small block used up MTRRs: */
range_basek = ALIGN(state->range_startk, gran_sizek); range_basek = ALIGN(state->range_startk, gran_sizek);
if ((range_basek > basek) && basek) if ((range_basek > basek) && basek)
return second_sizek; return second_sizek;
state->range_sizek -= (range_basek - state->range_startk); state->range_sizek -= (range_basek - state->range_startk);
range_sizek = ALIGN(state->range_sizek, gran_sizek); range_sizek = ALIGN(state->range_sizek, gran_sizek);
...@@ -439,14 +443,13 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -439,14 +443,13 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
} }
state->range_sizek = range_sizek; state->range_sizek = range_sizek;
/* try to append some small hole */ /* Try to append some small hole: */
range0_basek = state->range_startk; range0_basek = state->range_startk;
range0_sizek = ALIGN(state->range_sizek, chunk_sizek); range0_sizek = ALIGN(state->range_sizek, chunk_sizek);
/* no increase */ /* No increase: */
if (range0_sizek == state->range_sizek) { if (range0_sizek == state->range_sizek) {
if (debug_print) Dprintk("rangeX: %016lx - %016lx\n",
printk(KERN_DEBUG "rangeX: %016lx - %016lx\n",
range0_basek<<10, range0_basek<<10,
(range0_basek + state->range_sizek)<<10); (range0_basek + state->range_sizek)<<10);
state->reg = range_to_mtrr(state->reg, range0_basek, state->reg = range_to_mtrr(state->reg, range0_basek,
...@@ -454,7 +457,7 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -454,7 +457,7 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
return 0; return 0;
} }
/* only cut back, when it is not the last */ /* Only cut back when it is not the last: */
if (sizek) { if (sizek) {
while (range0_basek + range0_sizek > (basek + sizek)) { while (range0_basek + range0_sizek > (basek + sizek)) {
if (range0_sizek >= chunk_sizek) if (range0_sizek >= chunk_sizek)
...@@ -470,16 +473,16 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -470,16 +473,16 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
second_try: second_try:
range_basek = range0_basek + range0_sizek; range_basek = range0_basek + range0_sizek;
/* one hole in the middle */ /* One hole in the middle: */
if (range_basek > basek && range_basek <= (basek + sizek)) if (range_basek > basek && range_basek <= (basek + sizek))
second_sizek = range_basek - basek; second_sizek = range_basek - basek;
if (range0_sizek > state->range_sizek) { if (range0_sizek > state->range_sizek) {
/* one hole in middle or at end */ /* One hole in middle or at the end: */
hole_sizek = range0_sizek - state->range_sizek - second_sizek; hole_sizek = range0_sizek - state->range_sizek - second_sizek;
/* hole size should be less than half of range0 size */ /* Hole size should be less than half of range0 size: */
if (hole_sizek >= (range0_sizek >> 1) && if (hole_sizek >= (range0_sizek >> 1) &&
range0_sizek >= chunk_sizek) { range0_sizek >= chunk_sizek) {
range0_sizek -= chunk_sizek; range0_sizek -= chunk_sizek;
...@@ -491,8 +494,7 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -491,8 +494,7 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
} }
if (range0_sizek) { if (range0_sizek) {
if (debug_print) Dprintk("range0: %016lx - %016lx\n",
printk(KERN_DEBUG "range0: %016lx - %016lx\n",
range0_basek<<10, range0_basek<<10,
(range0_basek + range0_sizek)<<10); (range0_basek + range0_sizek)<<10);
state->reg = range_to_mtrr(state->reg, range0_basek, state->reg = range_to_mtrr(state->reg, range0_basek,
...@@ -500,21 +502,20 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek, ...@@ -500,21 +502,20 @@ range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
} }
if (range0_sizek < state->range_sizek) { if (range0_sizek < state->range_sizek) {
/* need to handle left over */ /* Need to handle left over range: */
range_sizek = state->range_sizek - range0_sizek; range_sizek = state->range_sizek - range0_sizek;
if (debug_print) Dprintk("range: %016lx - %016lx\n",
printk(KERN_DEBUG "range: %016lx - %016lx\n",
range_basek<<10, range_basek<<10,
(range_basek + range_sizek)<<10); (range_basek + range_sizek)<<10);
state->reg = range_to_mtrr(state->reg, range_basek, state->reg = range_to_mtrr(state->reg, range_basek,
range_sizek, MTRR_TYPE_WRBACK); range_sizek, MTRR_TYPE_WRBACK);
} }
if (hole_sizek) { if (hole_sizek) {
hole_basek = range_basek - hole_sizek - second_sizek; hole_basek = range_basek - hole_sizek - second_sizek;
if (debug_print) Dprintk("hole: %016lx - %016lx\n",
printk(KERN_DEBUG "hole: %016lx - %016lx\n",
hole_basek<<10, hole_basek<<10,
(hole_basek + hole_sizek)<<10); (hole_basek + hole_sizek)<<10);
state->reg = range_to_mtrr(state->reg, hole_basek, state->reg = range_to_mtrr(state->reg, hole_basek,
...@@ -537,23 +538,23 @@ set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn, ...@@ -537,23 +538,23 @@ set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn,
basek = base_pfn << (PAGE_SHIFT - 10); basek = base_pfn << (PAGE_SHIFT - 10);
sizek = size_pfn << (PAGE_SHIFT - 10); sizek = size_pfn << (PAGE_SHIFT - 10);
/* See if I can merge with the last range */ /* See if I can merge with the last range: */
if ((basek <= 1024) || if ((basek <= 1024) ||
(state->range_startk + state->range_sizek == basek)) { (state->range_startk + state->range_sizek == basek)) {
unsigned long endk = basek + sizek; unsigned long endk = basek + sizek;
state->range_sizek = endk - state->range_startk; state->range_sizek = endk - state->range_startk;
return; return;
} }
/* Write the range mtrrs */ /* Write the range mtrrs: */
if (state->range_sizek != 0) if (state->range_sizek != 0)
second_sizek = range_to_mtrr_with_hole(state, basek, sizek); second_sizek = range_to_mtrr_with_hole(state, basek, sizek);
/* Allocate an msr */ /* Allocate an msr: */
state->range_startk = basek + second_sizek; state->range_startk = basek + second_sizek;
state->range_sizek = sizek - second_sizek; state->range_sizek = sizek - second_sizek;
} }
/* mininum size of mtrr block that can take hole */ /* Mininum size of mtrr block that can take hole: */
static u64 mtrr_chunk_size __initdata = (256ULL<<20); static u64 mtrr_chunk_size __initdata = (256ULL<<20);
static int __init parse_mtrr_chunk_size_opt(char *p) static int __init parse_mtrr_chunk_size_opt(char *p)
...@@ -565,7 +566,7 @@ static int __init parse_mtrr_chunk_size_opt(char *p) ...@@ -565,7 +566,7 @@ static int __init parse_mtrr_chunk_size_opt(char *p)
} }
early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt); early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt);
/* granity of mtrr of block */ /* Granularity of mtrr of block: */
static u64 mtrr_gran_size __initdata; static u64 mtrr_gran_size __initdata;
static int __init parse_mtrr_gran_size_opt(char *p) static int __init parse_mtrr_gran_size_opt(char *p)
...@@ -577,7 +578,7 @@ static int __init parse_mtrr_gran_size_opt(char *p) ...@@ -577,7 +578,7 @@ static int __init parse_mtrr_gran_size_opt(char *p)
} }
early_param("mtrr_gran_size", parse_mtrr_gran_size_opt); early_param("mtrr_gran_size", parse_mtrr_gran_size_opt);
static int nr_mtrr_spare_reg __initdata = static unsigned long nr_mtrr_spare_reg __initdata =
CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT; CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT;
static int __init parse_mtrr_spare_reg(char *arg) static int __init parse_mtrr_spare_reg(char *arg)
...@@ -586,7 +587,6 @@ static int __init parse_mtrr_spare_reg(char *arg) ...@@ -586,7 +587,6 @@ static int __init parse_mtrr_spare_reg(char *arg)
nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0); nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0);
return 0; return 0;
} }
early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg); early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg);
static int __init static int __init
...@@ -594,8 +594,8 @@ x86_setup_var_mtrrs(struct res_range *range, int nr_range, ...@@ -594,8 +594,8 @@ x86_setup_var_mtrrs(struct res_range *range, int nr_range,
u64 chunk_size, u64 gran_size) u64 chunk_size, u64 gran_size)
{ {
struct var_mtrr_state var_state; struct var_mtrr_state var_state;
int i;
int num_reg; int num_reg;
int i;
var_state.range_startk = 0; var_state.range_startk = 0;
var_state.range_sizek = 0; var_state.range_sizek = 0;
...@@ -605,17 +605,18 @@ x86_setup_var_mtrrs(struct res_range *range, int nr_range, ...@@ -605,17 +605,18 @@ x86_setup_var_mtrrs(struct res_range *range, int nr_range,
memset(range_state, 0, sizeof(range_state)); memset(range_state, 0, sizeof(range_state));
/* Write the range etc */ /* Write the range: */
for (i = 0; i < nr_range; i++) for (i = 0; i < nr_range; i++) {
set_var_mtrr_range(&var_state, range[i].start, set_var_mtrr_range(&var_state, range[i].start,
range[i].end - range[i].start + 1); range[i].end - range[i].start + 1);
}
/* Write the last range */ /* Write the last range: */
if (var_state.range_sizek != 0) if (var_state.range_sizek != 0)
range_to_mtrr_with_hole(&var_state, 0, 0); range_to_mtrr_with_hole(&var_state, 0, 0);
num_reg = var_state.reg; num_reg = var_state.reg;
/* Clear out the extra MTRR's */ /* Clear out the extra MTRR's: */
while (var_state.reg < num_var_ranges) { while (var_state.reg < num_var_ranges) {
save_var_mtrr(var_state.reg, 0, 0, 0); save_var_mtrr(var_state.reg, 0, 0, 0);
var_state.reg++; var_state.reg++;
...@@ -645,10 +646,10 @@ static unsigned long __initdata min_loss_pfn[RANGE_NUM]; ...@@ -645,10 +646,10 @@ static unsigned long __initdata min_loss_pfn[RANGE_NUM];
static void __init print_out_mtrr_range_state(void) static void __init print_out_mtrr_range_state(void)
{ {
int i;
char start_factor = 'K', size_factor = 'K'; char start_factor = 'K', size_factor = 'K';
unsigned long start_base, size_base; unsigned long start_base, size_base;
mtrr_type type; mtrr_type type;
int i;
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
...@@ -676,10 +677,10 @@ static int __init mtrr_need_cleanup(void) ...@@ -676,10 +677,10 @@ static int __init mtrr_need_cleanup(void)
int i; int i;
mtrr_type type; mtrr_type type;
unsigned long size; unsigned long size;
/* extra one for all 0 */ /* Extra one for all 0: */
int num[MTRR_NUM_TYPES + 1]; int num[MTRR_NUM_TYPES + 1];
/* check entries number */ /* Check entries number: */
memset(num, 0, sizeof(num)); memset(num, 0, sizeof(num));
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
type = range_state[i].type; type = range_state[i].type;
...@@ -693,11 +694,11 @@ static int __init mtrr_need_cleanup(void) ...@@ -693,11 +694,11 @@ static int __init mtrr_need_cleanup(void)
num[type]++; num[type]++;
} }
/* check if we got UC entries */ /* Check if we got UC entries: */
if (!num[MTRR_TYPE_UNCACHABLE]) if (!num[MTRR_TYPE_UNCACHABLE])
return 0; return 0;
/* check if we only had WB and UC */ /* Check if we only had WB and UC */
if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
num_var_ranges - num[MTRR_NUM_TYPES]) num_var_ranges - num[MTRR_NUM_TYPES])
return 0; return 0;
...@@ -706,75 +707,73 @@ static int __init mtrr_need_cleanup(void) ...@@ -706,75 +707,73 @@ static int __init mtrr_need_cleanup(void)
} }
static unsigned long __initdata range_sums; static unsigned long __initdata range_sums;
static void __init mtrr_calc_range_state(u64 chunk_size, u64 gran_size,
unsigned long extra_remove_base, static void __init
unsigned long extra_remove_size, mtrr_calc_range_state(u64 chunk_size, u64 gran_size,
int i) unsigned long x_remove_base,
unsigned long x_remove_size, int i)
{ {
int num_reg;
static struct res_range range_new[RANGE_NUM]; static struct res_range range_new[RANGE_NUM];
static int nr_range_new;
unsigned long range_sums_new; unsigned long range_sums_new;
static int nr_range_new;
int num_reg;
/* convert ranges to var ranges state */ /* Convert ranges to var ranges state: */
num_reg = x86_setup_var_mtrrs(range, nr_range, num_reg = x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
chunk_size, gran_size);
/* we got new setting in range_state, check it */ /* We got new setting in range_state, check it: */
memset(range_new, 0, sizeof(range_new)); memset(range_new, 0, sizeof(range_new));
nr_range_new = x86_get_mtrr_mem_range(range_new, 0, nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
extra_remove_base, extra_remove_size); x_remove_base, x_remove_size);
range_sums_new = sum_ranges(range_new, nr_range_new); range_sums_new = sum_ranges(range_new, nr_range_new);
result[i].chunk_sizek = chunk_size >> 10; result[i].chunk_sizek = chunk_size >> 10;
result[i].gran_sizek = gran_size >> 10; result[i].gran_sizek = gran_size >> 10;
result[i].num_reg = num_reg; result[i].num_reg = num_reg;
if (range_sums < range_sums_new) { if (range_sums < range_sums_new) {
result[i].lose_cover_sizek = result[i].lose_cover_sizek = (range_sums_new - range_sums) << PSHIFT;
(range_sums_new - range_sums) << PSHIFT;
result[i].bad = 1; result[i].bad = 1;
} else } else {
result[i].lose_cover_sizek = result[i].lose_cover_sizek = (range_sums - range_sums_new) << PSHIFT;
(range_sums - range_sums_new) << PSHIFT; }
/* double check it */ /* Double check it: */
if (!result[i].bad && !result[i].lose_cover_sizek) { if (!result[i].bad && !result[i].lose_cover_sizek) {
if (nr_range_new != nr_range || if (nr_range_new != nr_range || memcmp(range, range_new, sizeof(range)))
memcmp(range, range_new, sizeof(range)))
result[i].bad = 1; result[i].bad = 1;
} }
if (!result[i].bad && (range_sums - range_sums_new < if (!result[i].bad && (range_sums - range_sums_new < min_loss_pfn[num_reg]))
min_loss_pfn[num_reg])) { min_loss_pfn[num_reg] = range_sums - range_sums_new;
min_loss_pfn[num_reg] =
range_sums - range_sums_new;
}
} }
static void __init mtrr_print_out_one_result(int i) static void __init mtrr_print_out_one_result(int i)
{ {
char gran_factor, chunk_factor, lose_factor;
unsigned long gran_base, chunk_base, lose_base; unsigned long gran_base, chunk_base, lose_base;
char gran_factor, chunk_factor, lose_factor;
gran_base = to_size_factor(result[i].gran_sizek, &gran_factor), gran_base = to_size_factor(result[i].gran_sizek, &gran_factor),
chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor), chunk_base = to_size_factor(result[i].chunk_sizek, &chunk_factor),
lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor), lose_base = to_size_factor(result[i].lose_cover_sizek, &lose_factor),
printk(KERN_INFO "%sgran_size: %ld%c \tchunk_size: %ld%c \t",
pr_info("%sgran_size: %ld%c \tchunk_size: %ld%c \t",
result[i].bad ? "*BAD*" : " ", result[i].bad ? "*BAD*" : " ",
gran_base, gran_factor, chunk_base, chunk_factor); gran_base, gran_factor, chunk_base, chunk_factor);
printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ld%c\n", pr_cont("num_reg: %d \tlose cover RAM: %s%ld%c\n",
result[i].num_reg, result[i].bad ? "-" : "", result[i].num_reg, result[i].bad ? "-" : "",
lose_base, lose_factor); lose_base, lose_factor);
} }
static int __init mtrr_search_optimal_index(void) static int __init mtrr_search_optimal_index(void)
{ {
int i;
int num_reg_good; int num_reg_good;
int index_good; int index_good;
int i;
if (nr_mtrr_spare_reg >= num_var_ranges) if (nr_mtrr_spare_reg >= num_var_ranges)
nr_mtrr_spare_reg = num_var_ranges - 1; nr_mtrr_spare_reg = num_var_ranges - 1;
num_reg_good = -1; num_reg_good = -1;
for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) { for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) {
if (!min_loss_pfn[i]) if (!min_loss_pfn[i])
...@@ -796,24 +795,24 @@ static int __init mtrr_search_optimal_index(void) ...@@ -796,24 +795,24 @@ static int __init mtrr_search_optimal_index(void)
return index_good; return index_good;
} }
int __init mtrr_cleanup(unsigned address_bits) int __init mtrr_cleanup(unsigned address_bits)
{ {
unsigned long extra_remove_base, extra_remove_size; unsigned long x_remove_base, x_remove_size;
unsigned long base, size, def, dummy; unsigned long base, size, def, dummy;
mtrr_type type;
u64 chunk_size, gran_size; u64 chunk_size, gran_size;
mtrr_type type;
int index_good; int index_good;
int i; int i;
if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1) if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
return 0; return 0;
rdmsr(MSR_MTRRdefType, def, dummy); rdmsr(MSR_MTRRdefType, def, dummy);
def &= 0xff; def &= 0xff;
if (def != MTRR_TYPE_UNCACHABLE) if (def != MTRR_TYPE_UNCACHABLE)
return 0; return 0;
/* get it and store it aside */ /* Get it and store it aside: */
memset(range_state, 0, sizeof(range_state)); memset(range_state, 0, sizeof(range_state));
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
mtrr_if->get(i, &base, &size, &type); mtrr_if->get(i, &base, &size, &type);
...@@ -822,29 +821,28 @@ int __init mtrr_cleanup(unsigned address_bits) ...@@ -822,29 +821,28 @@ int __init mtrr_cleanup(unsigned address_bits)
range_state[i].type = type; range_state[i].type = type;
} }
/* check if we need handle it and can handle it */ /* Check if we need handle it and can handle it: */
if (!mtrr_need_cleanup()) if (!mtrr_need_cleanup())
return 0; return 0;
/* print original var MTRRs at first, for debugging: */ /* Print original var MTRRs at first, for debugging: */
printk(KERN_DEBUG "original variable MTRRs\n"); printk(KERN_DEBUG "original variable MTRRs\n");
print_out_mtrr_range_state(); print_out_mtrr_range_state();
memset(range, 0, sizeof(range)); memset(range, 0, sizeof(range));
extra_remove_size = 0; x_remove_size = 0;
extra_remove_base = 1 << (32 - PAGE_SHIFT); x_remove_base = 1 << (32 - PAGE_SHIFT);
if (mtrr_tom2) if (mtrr_tom2)
extra_remove_size = x_remove_size = (mtrr_tom2 >> PAGE_SHIFT) - x_remove_base;
(mtrr_tom2 >> PAGE_SHIFT) - extra_remove_base;
nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base, nr_range = x86_get_mtrr_mem_range(range, 0, x_remove_base, x_remove_size);
extra_remove_size);
/* /*
* [0, 1M) should always be coverred by var mtrr with WB * [0, 1M) should always be covered by var mtrr with WB
* and fixed mtrrs should take effective before var mtrr for it * and fixed mtrrs should take effect before var mtrr for it:
*/ */
nr_range = add_range_with_merge(range, nr_range, 0, nr_range = add_range_with_merge(range, nr_range, 0,
(1ULL<<(20 - PAGE_SHIFT)) - 1); (1ULL<<(20 - PAGE_SHIFT)) - 1);
/* sort the ranges */ /* Sort the ranges: */
sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL); sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL);
range_sums = sum_ranges(range, nr_range); range_sums = sum_ranges(range, nr_range);
...@@ -854,7 +852,7 @@ int __init mtrr_cleanup(unsigned address_bits) ...@@ -854,7 +852,7 @@ int __init mtrr_cleanup(unsigned address_bits)
if (mtrr_chunk_size && mtrr_gran_size) { if (mtrr_chunk_size && mtrr_gran_size) {
i = 0; i = 0;
mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size, mtrr_calc_range_state(mtrr_chunk_size, mtrr_gran_size,
extra_remove_base, extra_remove_size, i); x_remove_base, x_remove_size, i);
mtrr_print_out_one_result(i); mtrr_print_out_one_result(i);
...@@ -880,7 +878,7 @@ int __init mtrr_cleanup(unsigned address_bits) ...@@ -880,7 +878,7 @@ int __init mtrr_cleanup(unsigned address_bits)
continue; continue;
mtrr_calc_range_state(chunk_size, gran_size, mtrr_calc_range_state(chunk_size, gran_size,
extra_remove_base, extra_remove_size, i); x_remove_base, x_remove_size, i);
if (debug_print) { if (debug_print) {
mtrr_print_out_one_result(i); mtrr_print_out_one_result(i);
printk(KERN_INFO "\n"); printk(KERN_INFO "\n");
...@@ -890,7 +888,7 @@ int __init mtrr_cleanup(unsigned address_bits) ...@@ -890,7 +888,7 @@ int __init mtrr_cleanup(unsigned address_bits)
} }
} }
/* try to find the optimal index */ /* Try to find the optimal index: */
index_good = mtrr_search_optimal_index(); index_good = mtrr_search_optimal_index();
if (index_good != -1) { if (index_good != -1) {
...@@ -898,7 +896,7 @@ int __init mtrr_cleanup(unsigned address_bits) ...@@ -898,7 +896,7 @@ int __init mtrr_cleanup(unsigned address_bits)
i = index_good; i = index_good;
mtrr_print_out_one_result(i); mtrr_print_out_one_result(i);
/* convert ranges to var ranges state */ /* Convert ranges to var ranges state: */
chunk_size = result[i].chunk_sizek; chunk_size = result[i].chunk_sizek;
chunk_size <<= 10; chunk_size <<= 10;
gran_size = result[i].gran_sizek; gran_size = result[i].gran_sizek;
...@@ -952,7 +950,7 @@ int __init amd_special_default_mtrr(void) ...@@ -952,7 +950,7 @@ int __init amd_special_default_mtrr(void)
return 0; return 0;
if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11) if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11)
return 0; return 0;
/* In case some hypervisor doesn't pass SYSCFG through */ /* In case some hypervisor doesn't pass SYSCFG through: */
if (rdmsr_safe(MSR_K8_SYSCFG, &l, &h) < 0) if (rdmsr_safe(MSR_K8_SYSCFG, &l, &h) < 0)
return 0; return 0;
/* /*
...@@ -965,19 +963,21 @@ int __init amd_special_default_mtrr(void) ...@@ -965,19 +963,21 @@ int __init amd_special_default_mtrr(void)
return 0; return 0;
} }
static u64 __init real_trim_memory(unsigned long start_pfn, static u64 __init
unsigned long limit_pfn) real_trim_memory(unsigned long start_pfn, unsigned long limit_pfn)
{ {
u64 trim_start, trim_size; u64 trim_start, trim_size;
trim_start = start_pfn; trim_start = start_pfn;
trim_start <<= PAGE_SHIFT; trim_start <<= PAGE_SHIFT;
trim_size = limit_pfn; trim_size = limit_pfn;
trim_size <<= PAGE_SHIFT; trim_size <<= PAGE_SHIFT;
trim_size -= trim_start; trim_size -= trim_start;
return e820_update_range(trim_start, trim_size, E820_RAM, return e820_update_range(trim_start, trim_size, E820_RAM, E820_RESERVED);
E820_RESERVED);
} }
/** /**
* mtrr_trim_uncached_memory - trim RAM not covered by MTRRs * mtrr_trim_uncached_memory - trim RAM not covered by MTRRs
* @end_pfn: ending page frame number * @end_pfn: ending page frame number
...@@ -994,21 +994,22 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -994,21 +994,22 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
unsigned long i, base, size, highest_pfn = 0, def, dummy; unsigned long i, base, size, highest_pfn = 0, def, dummy;
mtrr_type type; mtrr_type type;
u64 total_trim_size; u64 total_trim_size;
/* extra one for all 0 */ /* extra one for all 0 */
int num[MTRR_NUM_TYPES + 1]; int num[MTRR_NUM_TYPES + 1];
/* /*
* Make sure we only trim uncachable memory on machines that * Make sure we only trim uncachable memory on machines that
* support the Intel MTRR architecture: * support the Intel MTRR architecture:
*/ */
if (!is_cpu(INTEL) || disable_mtrr_trim) if (!is_cpu(INTEL) || disable_mtrr_trim)
return 0; return 0;
rdmsr(MSR_MTRRdefType, def, dummy); rdmsr(MSR_MTRRdefType, def, dummy);
def &= 0xff; def &= 0xff;
if (def != MTRR_TYPE_UNCACHABLE) if (def != MTRR_TYPE_UNCACHABLE)
return 0; return 0;
/* get it and store it aside */ /* Get it and store it aside: */
memset(range_state, 0, sizeof(range_state)); memset(range_state, 0, sizeof(range_state));
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
mtrr_if->get(i, &base, &size, &type); mtrr_if->get(i, &base, &size, &type);
...@@ -1017,7 +1018,7 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -1017,7 +1018,7 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
range_state[i].type = type; range_state[i].type = type;
} }
/* Find highest cached pfn */ /* Find highest cached pfn: */
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
type = range_state[i].type; type = range_state[i].type;
if (type != MTRR_TYPE_WRBACK) if (type != MTRR_TYPE_WRBACK)
...@@ -1028,13 +1029,13 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -1028,13 +1029,13 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
highest_pfn = base + size; highest_pfn = base + size;
} }
/* kvm/qemu doesn't have mtrr set right, don't trim them all */ /* kvm/qemu doesn't have mtrr set right, don't trim them all: */
if (!highest_pfn) { if (!highest_pfn) {
printk(KERN_INFO "CPU MTRRs all blank - virtualized system.\n"); printk(KERN_INFO "CPU MTRRs all blank - virtualized system.\n");
return 0; return 0;
} }
/* check entries number */ /* Check entries number: */
memset(num, 0, sizeof(num)); memset(num, 0, sizeof(num));
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
type = range_state[i].type; type = range_state[i].type;
...@@ -1046,11 +1047,11 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -1046,11 +1047,11 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
num[type]++; num[type]++;
} }
/* no entry for WB? */ /* No entry for WB? */
if (!num[MTRR_TYPE_WRBACK]) if (!num[MTRR_TYPE_WRBACK])
return 0; return 0;
/* check if we only had WB and UC */ /* Check if we only had WB and UC: */
if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] != if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
num_var_ranges - num[MTRR_NUM_TYPES]) num_var_ranges - num[MTRR_NUM_TYPES])
return 0; return 0;
...@@ -1066,31 +1067,31 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -1066,31 +1067,31 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
} }
nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0); nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0);
/* Check the head: */
total_trim_size = 0; total_trim_size = 0;
/* check the head */
if (range[0].start) if (range[0].start)
total_trim_size += real_trim_memory(0, range[0].start); total_trim_size += real_trim_memory(0, range[0].start);
/* check the holes */
/* Check the holes: */
for (i = 0; i < nr_range - 1; i++) { for (i = 0; i < nr_range - 1; i++) {
if (range[i].end + 1 < range[i+1].start) if (range[i].end + 1 < range[i+1].start)
total_trim_size += real_trim_memory(range[i].end + 1, total_trim_size += real_trim_memory(range[i].end + 1,
range[i+1].start); range[i+1].start);
} }
/* check the top */
/* Check the top: */
i = nr_range - 1; i = nr_range - 1;
if (range[i].end + 1 < end_pfn) if (range[i].end + 1 < end_pfn)
total_trim_size += real_trim_memory(range[i].end + 1, total_trim_size += real_trim_memory(range[i].end + 1,
end_pfn); end_pfn);
if (total_trim_size) { if (total_trim_size) {
printk(KERN_WARNING "WARNING: BIOS bug: CPU MTRRs don't cover" pr_warning("WARNING: BIOS bug: CPU MTRRs don't cover all of memory, losing %lluMB of RAM.\n", total_trim_size >> 20);
" all of memory, losing %lluMB of RAM.\n",
total_trim_size >> 20);
if (!changed_by_mtrr_cleanup) if (!changed_by_mtrr_cleanup)
WARN_ON(1); WARN_ON(1);
printk(KERN_INFO "update e820 for mtrr\n"); pr_info("update e820 for mtrr\n");
update_e820(); update_e820();
return 1; return 1;
...@@ -1098,4 +1099,3 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -1098,4 +1099,3 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
return 0; return 0;
} }
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
#include <asm/io.h>
#include <asm/processor-cyrix.h> #include <asm/processor-cyrix.h>
#include <asm/processor-flags.h> #include <asm/processor-flags.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
#include "mtrr.h" #include "mtrr.h"
static void static void
cyrix_get_arr(unsigned int reg, unsigned long *base, cyrix_get_arr(unsigned int reg, unsigned long *base,
unsigned long *size, mtrr_type * type) unsigned long *size, mtrr_type * type)
{ {
unsigned long flags;
unsigned char arr, ccr3, rcr, shift; unsigned char arr, ccr3, rcr, shift;
unsigned long flags;
arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */
/* Save flags and disable interrupts */
local_irq_save(flags); local_irq_save(flags);
ccr3 = getCx86(CX86_CCR3); ccr3 = getCx86(CX86_CCR3);
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
((unsigned char *) base)[3] = getCx86(arr); ((unsigned char *)base)[3] = getCx86(arr);
((unsigned char *) base)[2] = getCx86(arr + 1); ((unsigned char *)base)[2] = getCx86(arr + 1);
((unsigned char *) base)[1] = getCx86(arr + 2); ((unsigned char *)base)[1] = getCx86(arr + 2);
rcr = getCx86(CX86_RCR_BASE + reg); rcr = getCx86(CX86_RCR_BASE + reg);
setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ setCx86(CX86_CCR3, ccr3); /* disable MAPEN */
/* Enable interrupts if it was enabled previously */
local_irq_restore(flags); local_irq_restore(flags);
shift = ((unsigned char *) base)[1] & 0x0f; shift = ((unsigned char *) base)[1] & 0x0f;
*base >>= PAGE_SHIFT; *base >>= PAGE_SHIFT;
/* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 /*
* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7
* Note: shift==0xf means 4G, this is unsupported. * Note: shift==0xf means 4G, this is unsupported.
*/ */
if (shift) if (shift)
...@@ -76,17 +78,20 @@ cyrix_get_arr(unsigned int reg, unsigned long *base, ...@@ -76,17 +78,20 @@ cyrix_get_arr(unsigned int reg, unsigned long *base,
} }
} }
/*
* 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.
*/
static int static int
cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) 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; unsigned long lbase, lsize;
mtrr_type ltype;
int i;
switch (replace_reg) { switch (replace_reg) {
case 7: case 7:
...@@ -114,7 +119,10 @@ cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) ...@@ -114,7 +119,10 @@ cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)
if (lsize == 0) if (lsize == 0)
return i; return i;
} }
/* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ /*
* ARR0-ARR6 isn't free
* try ARR7 but its size must be at least 256K
*/
cyrix_get_arr(i, &lbase, &lsize, &ltype); cyrix_get_arr(i, &lbase, &lsize, &ltype);
if ((lsize == 0) && (size >= 0x40)) if ((lsize == 0) && (size >= 0x40))
return i; return i;
...@@ -122,21 +130,22 @@ cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) ...@@ -122,21 +130,22 @@ cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)
return -ENOSPC; return -ENOSPC;
} }
static u32 cr4 = 0; static u32 cr4, ccr3;
static u32 ccr3;
static void prepare_set(void) static void prepare_set(void)
{ {
u32 cr0; u32 cr0;
/* Save value of CR4 and clear Page Global Enable (bit 7) */ /* Save value of CR4 and clear Page Global Enable (bit 7) */
if ( cpu_has_pge ) { if (cpu_has_pge) {
cr4 = read_cr4(); cr4 = read_cr4();
write_cr4(cr4 & ~X86_CR4_PGE); write_cr4(cr4 & ~X86_CR4_PGE);
} }
/* Disable and flush caches. Note that wbinvd flushes the TLBs as /*
a side-effect */ * Disable and flush caches.
* Note that wbinvd flushes the TLBs as a side-effect
*/
cr0 = read_cr0() | X86_CR0_CD; cr0 = read_cr0() | X86_CR0_CD;
wbinvd(); wbinvd();
write_cr0(cr0); write_cr0(cr0);
...@@ -147,7 +156,6 @@ static void prepare_set(void) ...@@ -147,7 +156,6 @@ static void prepare_set(void)
/* Cyrix ARRs - everything else was excluded at the top */ /* Cyrix ARRs - everything else was excluded at the top */
setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);
} }
static void post_set(void) static void post_set(void)
...@@ -162,7 +170,7 @@ static void post_set(void) ...@@ -162,7 +170,7 @@ static void post_set(void)
write_cr0(read_cr0() & 0xbfffffff); write_cr0(read_cr0() & 0xbfffffff);
/* Restore value of CR4 */ /* Restore value of CR4 */
if ( cpu_has_pge ) if (cpu_has_pge)
write_cr4(cr4); write_cr4(cr4);
} }
...@@ -178,7 +186,8 @@ static void cyrix_set_arr(unsigned int reg, unsigned long base, ...@@ -178,7 +186,8 @@ static void cyrix_set_arr(unsigned int reg, unsigned long base,
size >>= 6; size >>= 6;
size &= 0x7fff; /* make sure arr_size <= 14 */ size &= 0x7fff; /* make sure arr_size <= 14 */
for (arr_size = 0; size; arr_size++, size >>= 1) ; for (arr_size = 0; size; arr_size++, size >>= 1)
;
if (reg < 7) { if (reg < 7) {
switch (type) { switch (type) {
...@@ -215,9 +224,9 @@ static void cyrix_set_arr(unsigned int reg, unsigned long base, ...@@ -215,9 +224,9 @@ static void cyrix_set_arr(unsigned int reg, unsigned long base,
prepare_set(); prepare_set();
base <<= PAGE_SHIFT; base <<= PAGE_SHIFT;
setCx86(arr, ((unsigned char *) &base)[3]); setCx86(arr + 0, ((unsigned char *)&base)[3]);
setCx86(arr + 1, ((unsigned char *) &base)[2]); setCx86(arr + 1, ((unsigned char *)&base)[2]);
setCx86(arr + 2, (((unsigned char *) &base)[1]) | arr_size); setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size);
setCx86(CX86_RCR_BASE + reg, arr_type); setCx86(CX86_RCR_BASE + reg, arr_type);
post_set(); post_set();
...@@ -247,16 +256,17 @@ static void cyrix_set_all(void) ...@@ -247,16 +256,17 @@ static void cyrix_set_all(void)
setCx86(CX86_CCR0 + i, ccr_state[i]); setCx86(CX86_CCR0 + i, ccr_state[i]);
for (; i < 7; i++) for (; i < 7; i++)
setCx86(CX86_CCR4 + i, ccr_state[i]); setCx86(CX86_CCR4 + i, ccr_state[i]);
for (i = 0; i < 8; i++)
for (i = 0; i < 8; i++) {
cyrix_set_arr(i, arr_state[i].base, cyrix_set_arr(i, arr_state[i].base,
arr_state[i].size, arr_state[i].type); arr_state[i].size, arr_state[i].type);
}
post_set(); post_set();
} }
static struct mtrr_ops cyrix_mtrr_ops = { static struct mtrr_ops cyrix_mtrr_ops = {
.vendor = X86_VENDOR_CYRIX, .vendor = X86_VENDOR_CYRIX,
// .init = cyrix_arr_init,
.set_all = cyrix_set_all, .set_all = cyrix_set_all,
.set = cyrix_set_arr, .set = cyrix_set_arr,
.get = cyrix_get_arr, .get = cyrix_get_arr,
...@@ -270,5 +280,3 @@ int __init cyrix_init_mtrr(void) ...@@ -270,5 +280,3 @@ int __init cyrix_init_mtrr(void)
set_mtrr_ops(&cyrix_mtrr_ops); set_mtrr_ops(&cyrix_mtrr_ops);
return 0; return 0;
} }
//arch_initcall(cyrix_init_mtrr);
/* This only handles 32bit MTRR on 32bit hosts. This is strictly wrong /*
because MTRRs can span upto 40 bits (36bits on most modern x86) */ * This only handles 32bit MTRR on 32bit hosts. This is strictly wrong
* because MTRRs can span upto 40 bits (36bits on most modern x86)
*/
#define DEBUG
#include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/io.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
#include <asm/system.h>
#include <asm/cpufeature.h>
#include <asm/processor-flags.h> #include <asm/processor-flags.h>
#include <asm/cpufeature.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/system.h>
#include <asm/mtrr.h>
#include <asm/msr.h>
#include <asm/pat.h> #include <asm/pat.h>
#include "mtrr.h" #include "mtrr.h"
struct fixed_range_block { struct fixed_range_block {
...@@ -30,10 +36,10 @@ static unsigned long smp_changes_mask; ...@@ -30,10 +36,10 @@ static unsigned long smp_changes_mask;
static int mtrr_state_set; static int mtrr_state_set;
u64 mtrr_tom2; u64 mtrr_tom2;
struct mtrr_state_type mtrr_state = {}; struct mtrr_state_type mtrr_state;
EXPORT_SYMBOL_GPL(mtrr_state); EXPORT_SYMBOL_GPL(mtrr_state);
/** /*
* BIOS is expected to clear MtrrFixDramModEn bit, see for example * BIOS is expected to clear MtrrFixDramModEn bit, see for example
* "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD * "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
* Opteron Processors" (26094 Rev. 3.30 February 2006), section * Opteron Processors" (26094 Rev. 3.30 February 2006), section
...@@ -104,9 +110,8 @@ u8 mtrr_type_lookup(u64 start, u64 end) ...@@ -104,9 +110,8 @@ u8 mtrr_type_lookup(u64 start, u64 end)
* Look of multiple ranges matching this address and pick type * Look of multiple ranges matching this address and pick type
* as per MTRR precedence * as per MTRR precedence
*/ */
if (!(mtrr_state.enabled & 2)) { if (!(mtrr_state.enabled & 2))
return mtrr_state.def_type; return mtrr_state.def_type;
}
prev_match = 0xFF; prev_match = 0xFF;
for (i = 0; i < num_var_ranges; ++i) { for (i = 0; i < num_var_ranges; ++i) {
...@@ -125,9 +130,8 @@ u8 mtrr_type_lookup(u64 start, u64 end) ...@@ -125,9 +130,8 @@ u8 mtrr_type_lookup(u64 start, u64 end)
if (start_state != end_state) if (start_state != end_state)
return 0xFE; return 0xFE;
if ((start & mask) != (base & mask)) { if ((start & mask) != (base & mask))
continue; continue;
}
curr_match = mtrr_state.var_ranges[i].base_lo & 0xff; curr_match = mtrr_state.var_ranges[i].base_lo & 0xff;
if (prev_match == 0xFF) { if (prev_match == 0xFF) {
...@@ -148,10 +152,9 @@ u8 mtrr_type_lookup(u64 start, u64 end) ...@@ -148,10 +152,9 @@ u8 mtrr_type_lookup(u64 start, u64 end)
curr_match = MTRR_TYPE_WRTHROUGH; curr_match = MTRR_TYPE_WRTHROUGH;
} }
if (prev_match != curr_match) { if (prev_match != curr_match)
return MTRR_TYPE_UNCACHABLE; return MTRR_TYPE_UNCACHABLE;
} }
}
if (mtrr_tom2) { if (mtrr_tom2) {
if (start >= (1ULL<<32) && (end < mtrr_tom2)) if (start >= (1ULL<<32) && (end < mtrr_tom2))
...@@ -172,7 +175,7 @@ get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr) ...@@ -172,7 +175,7 @@ get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr)
rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
} }
/* fill the MSR pair relating to a var range */ /* Fill the MSR pair relating to a var range */
void fill_mtrr_var_range(unsigned int index, void fill_mtrr_var_range(unsigned int index,
u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi) u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi)
{ {
...@@ -186,10 +189,9 @@ void fill_mtrr_var_range(unsigned int index, ...@@ -186,10 +189,9 @@ void fill_mtrr_var_range(unsigned int index,
vr[index].mask_hi = mask_hi; vr[index].mask_hi = mask_hi;
} }
static void static void get_fixed_ranges(mtrr_type *frs)
get_fixed_ranges(mtrr_type * frs)
{ {
unsigned int *p = (unsigned int *) frs; unsigned int *p = (unsigned int *)frs;
int i; int i;
k8_check_syscfg_dram_mod_en(); k8_check_syscfg_dram_mod_en();
...@@ -217,7 +219,7 @@ static void __init print_fixed_last(void) ...@@ -217,7 +219,7 @@ static void __init print_fixed_last(void)
if (!last_fixed_end) if (!last_fixed_end)
return; return;
printk(KERN_DEBUG " %05X-%05X %s\n", last_fixed_start, pr_debug(" %05X-%05X %s\n", last_fixed_start,
last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type)); last_fixed_end - 1, mtrr_attrib_to_str(last_fixed_type));
last_fixed_end = 0; last_fixed_end = 0;
...@@ -231,8 +233,8 @@ static void __init update_fixed_last(unsigned base, unsigned end, ...@@ -231,8 +233,8 @@ static void __init update_fixed_last(unsigned base, unsigned end,
last_fixed_type = type; last_fixed_type = type;
} }
static void __init print_fixed(unsigned base, unsigned step, static void __init
const mtrr_type *types) print_fixed(unsigned base, unsigned step, const mtrr_type *types)
{ {
unsigned i; unsigned i;
...@@ -259,30 +261,33 @@ static void __init print_mtrr_state(void) ...@@ -259,30 +261,33 @@ static void __init print_mtrr_state(void)
unsigned int i; unsigned int i;
int high_width; int high_width;
printk(KERN_DEBUG "MTRR default type: %s\n", pr_debug("MTRR default type: %s\n",
mtrr_attrib_to_str(mtrr_state.def_type)); mtrr_attrib_to_str(mtrr_state.def_type));
if (mtrr_state.have_fixed) { if (mtrr_state.have_fixed) {
printk(KERN_DEBUG "MTRR fixed ranges %sabled:\n", pr_debug("MTRR fixed ranges %sabled:\n",
mtrr_state.enabled & 1 ? "en" : "dis"); mtrr_state.enabled & 1 ? "en" : "dis");
print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0); print_fixed(0x00000, 0x10000, mtrr_state.fixed_ranges + 0);
for (i = 0; i < 2; ++i) for (i = 0; i < 2; ++i)
print_fixed(0x80000 + i * 0x20000, 0x04000, mtrr_state.fixed_ranges + (i + 1) * 8); print_fixed(0x80000 + i * 0x20000, 0x04000,
mtrr_state.fixed_ranges + (i + 1) * 8);
for (i = 0; i < 8; ++i) for (i = 0; i < 8; ++i)
print_fixed(0xC0000 + i * 0x08000, 0x01000, mtrr_state.fixed_ranges + (i + 3) * 8); print_fixed(0xC0000 + i * 0x08000, 0x01000,
mtrr_state.fixed_ranges + (i + 3) * 8);
/* tail */ /* tail */
print_fixed_last(); print_fixed_last();
} }
printk(KERN_DEBUG "MTRR variable ranges %sabled:\n", pr_debug("MTRR variable ranges %sabled:\n",
mtrr_state.enabled & 2 ? "en" : "dis"); mtrr_state.enabled & 2 ? "en" : "dis");
if (size_or_mask & 0xffffffffUL) if (size_or_mask & 0xffffffffUL)
high_width = ffs(size_or_mask & 0xffffffffUL) - 1; high_width = ffs(size_or_mask & 0xffffffffUL) - 1;
else else
high_width = ffs(size_or_mask>>32) + 32 - 1; high_width = ffs(size_or_mask>>32) + 32 - 1;
high_width = (high_width - (32 - PAGE_SHIFT) + 3) / 4; high_width = (high_width - (32 - PAGE_SHIFT) + 3) / 4;
for (i = 0; i < num_var_ranges; ++i) { for (i = 0; i < num_var_ranges; ++i) {
if (mtrr_state.var_ranges[i].mask_lo & (1 << 11)) if (mtrr_state.var_ranges[i].mask_lo & (1 << 11))
printk(KERN_DEBUG " %u base %0*X%05X000 mask %0*X%05X000 %s\n", pr_debug(" %u base %0*X%05X000 mask %0*X%05X000 %s\n",
i, i,
high_width, high_width,
mtrr_state.var_ranges[i].base_hi, mtrr_state.var_ranges[i].base_hi,
...@@ -292,21 +297,19 @@ static void __init print_mtrr_state(void) ...@@ -292,21 +297,19 @@ static void __init print_mtrr_state(void)
mtrr_state.var_ranges[i].mask_lo >> 12, mtrr_state.var_ranges[i].mask_lo >> 12,
mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 0xff)); mtrr_attrib_to_str(mtrr_state.var_ranges[i].base_lo & 0xff));
else else
printk(KERN_DEBUG " %u disabled\n", i); pr_debug(" %u disabled\n", i);
}
if (mtrr_tom2) {
printk(KERN_DEBUG "TOM2: %016llx aka %lldM\n",
mtrr_tom2, mtrr_tom2>>20);
} }
if (mtrr_tom2)
pr_debug("TOM2: %016llx aka %lldM\n", mtrr_tom2, mtrr_tom2>>20);
} }
/* Grab all of the MTRR state for this CPU into *state */ /* Grab all of the MTRR state for this CPU into *state */
void __init get_mtrr_state(void) void __init get_mtrr_state(void)
{ {
unsigned int i;
struct mtrr_var_range *vrs; struct mtrr_var_range *vrs;
unsigned lo, dummy;
unsigned long flags; unsigned long flags;
unsigned lo, dummy;
unsigned int i;
vrs = mtrr_state.var_ranges; vrs = mtrr_state.var_ranges;
...@@ -324,6 +327,7 @@ void __init get_mtrr_state(void) ...@@ -324,6 +327,7 @@ void __init get_mtrr_state(void)
if (amd_special_default_mtrr()) { if (amd_special_default_mtrr()) {
unsigned low, high; unsigned low, high;
/* TOP_MEM2 */ /* TOP_MEM2 */
rdmsr(MSR_K8_TOP_MEM2, low, high); rdmsr(MSR_K8_TOP_MEM2, low, high);
mtrr_tom2 = high; mtrr_tom2 = high;
...@@ -344,10 +348,9 @@ void __init get_mtrr_state(void) ...@@ -344,10 +348,9 @@ void __init get_mtrr_state(void)
post_set(); post_set();
local_irq_restore(flags); local_irq_restore(flags);
} }
/* Some BIOS's are fucked and don't set all MTRRs the same! */ /* Some BIOS's are messed up and don't set all MTRRs the same! */
void __init mtrr_state_warn(void) void __init mtrr_state_warn(void)
{ {
unsigned long mask = smp_changes_mask; unsigned long mask = smp_changes_mask;
...@@ -355,28 +358,33 @@ void __init mtrr_state_warn(void) ...@@ -355,28 +358,33 @@ void __init mtrr_state_warn(void)
if (!mask) if (!mask)
return; return;
if (mask & MTRR_CHANGE_MASK_FIXED) if (mask & MTRR_CHANGE_MASK_FIXED)
printk(KERN_WARNING "mtrr: your CPUs had inconsistent fixed MTRR settings\n"); pr_warning("mtrr: your CPUs had inconsistent fixed MTRR settings\n");
if (mask & MTRR_CHANGE_MASK_VARIABLE) if (mask & MTRR_CHANGE_MASK_VARIABLE)
printk(KERN_WARNING "mtrr: your CPUs had inconsistent variable MTRR settings\n"); pr_warning("mtrr: your CPUs had inconsistent variable MTRR settings\n");
if (mask & MTRR_CHANGE_MASK_DEFTYPE) if (mask & MTRR_CHANGE_MASK_DEFTYPE)
printk(KERN_WARNING "mtrr: your CPUs had inconsistent MTRRdefType settings\n"); pr_warning("mtrr: your CPUs had inconsistent MTRRdefType settings\n");
printk(KERN_INFO "mtrr: probably your BIOS does not setup all CPUs.\n"); printk(KERN_INFO "mtrr: probably your BIOS does not setup all CPUs.\n");
printk(KERN_INFO "mtrr: corrected configuration.\n"); printk(KERN_INFO "mtrr: corrected configuration.\n");
} }
/* Doesn't attempt to pass an error out to MTRR users /*
because it's quite complicated in some cases and probably not * Doesn't attempt to pass an error out to MTRR users
worth it because the best error handling is to ignore it. */ * because it's quite complicated in some cases and probably not
* worth it because the best error handling is to ignore it.
*/
void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b) void mtrr_wrmsr(unsigned msr, unsigned a, unsigned b)
{ {
if (wrmsr_safe(msr, a, b) < 0) if (wrmsr_safe(msr, a, b) < 0) {
printk(KERN_ERR printk(KERN_ERR
"MTRR: CPU %u: Writing MSR %x to %x:%x failed\n", "MTRR: CPU %u: Writing MSR %x to %x:%x failed\n",
smp_processor_id(), msr, a, b); smp_processor_id(), msr, a, b);
}
} }
/** /**
* set_fixed_range - checks & updates a fixed-range MTRR if it differs from the value it should have * set_fixed_range - checks & updates a fixed-range MTRR if it
* differs from the value it should have
* @msr: MSR address of the MTTR which should be checked and updated * @msr: MSR address of the MTTR which should be checked and updated
* @changed: pointer which indicates whether the MTRR needed to be changed * @changed: pointer which indicates whether the MTRR needed to be changed
* @msrwords: pointer to the MSR values which the MSR should have * @msrwords: pointer to the MSR values which the MSR should have
...@@ -401,20 +409,23 @@ static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords) ...@@ -401,20 +409,23 @@ static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords)
* *
* Returns: The index of the region on success, else negative on error. * Returns: The index of the region on success, else negative on error.
*/ */
int generic_get_free_region(unsigned long base, unsigned long size, int replace_reg) int
generic_get_free_region(unsigned long base, unsigned long size, int replace_reg)
{ {
int i, max;
mtrr_type ltype;
unsigned long lbase, lsize; unsigned long lbase, lsize;
mtrr_type ltype;
int i, max;
max = num_var_ranges; max = num_var_ranges;
if (replace_reg >= 0 && replace_reg < max) if (replace_reg >= 0 && replace_reg < max)
return replace_reg; return replace_reg;
for (i = 0; i < max; ++i) { for (i = 0; i < max; ++i) {
mtrr_if->get(i, &lbase, &lsize, &ltype); mtrr_if->get(i, &lbase, &lsize, &ltype);
if (lsize == 0) if (lsize == 0)
return i; return i;
} }
return -ENOSPC; return -ENOSPC;
} }
...@@ -471,27 +482,31 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, ...@@ -471,27 +482,31 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base,
} }
/** /**
* set_fixed_ranges - checks & updates the fixed-range MTRRs if they differ from the saved set * set_fixed_ranges - checks & updates the fixed-range MTRRs if they
* differ from the saved set
* @frs: pointer to fixed-range MTRR values, saved by get_fixed_ranges() * @frs: pointer to fixed-range MTRR values, saved by get_fixed_ranges()
*/ */
static int set_fixed_ranges(mtrr_type * frs) static int set_fixed_ranges(mtrr_type *frs)
{ {
unsigned long long *saved = (unsigned long long *) frs; unsigned long long *saved = (unsigned long long *)frs;
bool changed = false; bool changed = false;
int block=-1, range; int block = -1, range;
k8_check_syscfg_dram_mod_en(); k8_check_syscfg_dram_mod_en();
while (fixed_range_blocks[++block].ranges) while (fixed_range_blocks[++block].ranges) {
for (range=0; range < fixed_range_blocks[block].ranges; range++) for (range = 0; range < fixed_range_blocks[block].ranges; range++)
set_fixed_range(fixed_range_blocks[block].base_msr + range, set_fixed_range(fixed_range_blocks[block].base_msr + range,
&changed, (unsigned int *) saved++); &changed, (unsigned int *)saved++);
}
return changed; return changed;
} }
/* Set the MSR pair relating to a var range. Returns TRUE if /*
changes are made */ * Set the MSR pair relating to a var range.
* Returns true if changes are made.
*/
static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr) static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
{ {
unsigned int lo, hi; unsigned int lo, hi;
...@@ -501,6 +516,7 @@ static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr) ...@@ -501,6 +516,7 @@ static bool set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr)
if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
|| (vr->base_hi & (size_and_mask >> (32 - PAGE_SHIFT))) != || (vr->base_hi & (size_and_mask >> (32 - PAGE_SHIFT))) !=
(hi & (size_and_mask >> (32 - PAGE_SHIFT)))) { (hi & (size_and_mask >> (32 - PAGE_SHIFT)))) {
mtrr_wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); mtrr_wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
changed = true; changed = true;
} }
...@@ -526,21 +542,26 @@ static u32 deftype_lo, deftype_hi; ...@@ -526,21 +542,26 @@ static u32 deftype_lo, deftype_hi;
*/ */
static unsigned long set_mtrr_state(void) static unsigned long set_mtrr_state(void)
{ {
unsigned int i;
unsigned long change_mask = 0; unsigned long change_mask = 0;
unsigned int i;
for (i = 0; i < num_var_ranges; i++) for (i = 0; i < num_var_ranges; i++) {
if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i])) if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i]))
change_mask |= MTRR_CHANGE_MASK_VARIABLE; change_mask |= MTRR_CHANGE_MASK_VARIABLE;
}
if (mtrr_state.have_fixed && set_fixed_ranges(mtrr_state.fixed_ranges)) if (mtrr_state.have_fixed && set_fixed_ranges(mtrr_state.fixed_ranges))
change_mask |= MTRR_CHANGE_MASK_FIXED; change_mask |= MTRR_CHANGE_MASK_FIXED;
/* Set_mtrr_restore restores the old value of MTRRdefType, /*
so to set it we fiddle with the saved value */ * Set_mtrr_restore restores the old value of MTRRdefType,
* so to set it we fiddle with the saved value:
*/
if ((deftype_lo & 0xff) != mtrr_state.def_type if ((deftype_lo & 0xff) != mtrr_state.def_type
|| ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) { || ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) {
deftype_lo = (deftype_lo & ~0xcff) | mtrr_state.def_type | (mtrr_state.enabled << 10);
deftype_lo = (deftype_lo & ~0xcff) | mtrr_state.def_type |
(mtrr_state.enabled << 10);
change_mask |= MTRR_CHANGE_MASK_DEFTYPE; change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
} }
...@@ -548,23 +569,26 @@ static unsigned long set_mtrr_state(void) ...@@ -548,23 +569,26 @@ static unsigned long set_mtrr_state(void)
} }
static unsigned long cr4 = 0; static unsigned long cr4;
static DEFINE_SPINLOCK(set_atomicity_lock); static DEFINE_SPINLOCK(set_atomicity_lock);
/* /*
* Since we are disabling the cache don't allow any interrupts - they * Since we are disabling the cache don't allow any interrupts,
* would run extremely slow and would only increase the pain. The caller must * they would run extremely slow and would only increase the pain.
* ensure that local interrupts are disabled and are reenabled after post_set() *
* has been called. * The caller must ensure that local interrupts are disabled and
* are reenabled after post_set() has been called.
*/ */
static void prepare_set(void) __acquires(set_atomicity_lock) static void prepare_set(void) __acquires(set_atomicity_lock)
{ {
unsigned long cr0; unsigned long cr0;
/* Note that this is not ideal, since the cache is only flushed/disabled /*
for this CPU while the MTRRs are changed, but changing this requires * Note that this is not ideal
more invasive changes to the way the kernel boots */ * since the cache is only flushed/disabled for this CPU while the
* MTRRs are changed, but changing this requires more invasive
* changes to the way the kernel boots
*/
spin_lock(&set_atomicity_lock); spin_lock(&set_atomicity_lock);
...@@ -574,7 +598,7 @@ static void prepare_set(void) __acquires(set_atomicity_lock) ...@@ -574,7 +598,7 @@ static void prepare_set(void) __acquires(set_atomicity_lock)
wbinvd(); wbinvd();
/* Save value of CR4 and clear Page Global Enable (bit 7) */ /* Save value of CR4 and clear Page Global Enable (bit 7) */
if ( cpu_has_pge ) { if (cpu_has_pge) {
cr4 = read_cr4(); cr4 = read_cr4();
write_cr4(cr4 & ~X86_CR4_PGE); write_cr4(cr4 & ~X86_CR4_PGE);
} }
...@@ -601,7 +625,7 @@ static void post_set(void) __releases(set_atomicity_lock) ...@@ -601,7 +625,7 @@ static void post_set(void) __releases(set_atomicity_lock)
write_cr0(read_cr0() & 0xbfffffff); write_cr0(read_cr0() & 0xbfffffff);
/* Restore value of CR4 */ /* Restore value of CR4 */
if ( cpu_has_pge ) if (cpu_has_pge)
write_cr4(cr4); write_cr4(cr4);
spin_unlock(&set_atomicity_lock); spin_unlock(&set_atomicity_lock);
} }
...@@ -632,15 +656,18 @@ static void generic_set_all(void) ...@@ -632,15 +656,18 @@ static void generic_set_all(void)
} }
/**
* generic_set_mtrr - set variable MTRR register on the local CPU.
*
* @reg: The register to set.
* @base: The base address of the region.
* @size: The size of the region. If this is 0 the region is disabled.
* @type: The type of the region.
*
* Returns nothing.
*/
static void generic_set_mtrr(unsigned int reg, unsigned long base, static void generic_set_mtrr(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type) unsigned long size, mtrr_type type)
/* [SUMMARY] Set variable MTRR register on the local CPU.
<reg> The register to set.
<base> The base address of the region.
<size> The size of the region. If this is 0 the region is disabled.
<type> The type of the region.
[RETURNS] Nothing.
*/
{ {
unsigned long flags; unsigned long flags;
struct mtrr_var_range *vr; struct mtrr_var_range *vr;
...@@ -651,8 +678,10 @@ static void generic_set_mtrr(unsigned int reg, unsigned long base, ...@@ -651,8 +678,10 @@ static void generic_set_mtrr(unsigned int reg, unsigned long base,
prepare_set(); prepare_set();
if (size == 0) { if (size == 0) {
/* The invalid bit is kept in the mask, so we simply clear the /*
relevant mask register to disable a range. */ * The invalid bit is kept in the mask, so we simply
* clear the relevant mask register to disable a range.
*/
mtrr_wrmsr(MTRRphysMask_MSR(reg), 0, 0); mtrr_wrmsr(MTRRphysMask_MSR(reg), 0, 0);
memset(vr, 0, sizeof(struct mtrr_var_range)); memset(vr, 0, sizeof(struct mtrr_var_range));
} else { } else {
...@@ -669,46 +698,50 @@ static void generic_set_mtrr(unsigned int reg, unsigned long base, ...@@ -669,46 +698,50 @@ static void generic_set_mtrr(unsigned int reg, unsigned long base,
local_irq_restore(flags); local_irq_restore(flags);
} }
int generic_validate_add_page(unsigned long base, unsigned long size, unsigned int type) int generic_validate_add_page(unsigned long base, unsigned long size,
unsigned int type)
{ {
unsigned long lbase, last; unsigned long lbase, last;
/* For Intel PPro stepping <= 7, must be 4 MiB aligned /*
and not touch 0x70000000->0x7003FFFF */ * For Intel PPro stepping <= 7
* must be 4 MiB aligned and not touch 0x70000000 -> 0x7003FFFF
*/
if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 && if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 &&
boot_cpu_data.x86_model == 1 && boot_cpu_data.x86_model == 1 &&
boot_cpu_data.x86_mask <= 7) { boot_cpu_data.x86_mask <= 7) {
if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) { if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) {
printk(KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); pr_warning("mtrr: base(0x%lx000) is not 4 MiB aligned\n", base);
return -EINVAL; return -EINVAL;
} }
if (!(base + size < 0x70000 || base > 0x7003F) && if (!(base + size < 0x70000 || base > 0x7003F) &&
(type == MTRR_TYPE_WRCOMB (type == MTRR_TYPE_WRCOMB
|| type == MTRR_TYPE_WRBACK)) { || type == MTRR_TYPE_WRBACK)) {
printk(KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); pr_warning("mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n");
return -EINVAL; return -EINVAL;
} }
} }
/* Check upper bits of base and last are equal and lower bits are 0 /*
for base and 1 for last */ * Check upper bits of base and last are equal and lower bits are 0
* for base and 1 for last
*/
last = base + size - 1; last = base + size - 1;
for (lbase = base; !(lbase & 1) && (last & 1); for (lbase = base; !(lbase & 1) && (last & 1);
lbase = lbase >> 1, last = last >> 1) ; lbase = lbase >> 1, last = last >> 1)
;
if (lbase != last) { if (lbase != last) {
printk(KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", pr_warning("mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", base, size);
base, size);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static int generic_have_wrcomb(void) static int generic_have_wrcomb(void)
{ {
unsigned long config, dummy; unsigned long config, dummy;
rdmsr(MSR_MTRRcap, config, dummy); rdmsr(MSR_MTRRcap, config, dummy);
return (config & (1 << 10)); return config & (1 << 10);
} }
int positive_have_wrcomb(void) int positive_have_wrcomb(void)
...@@ -716,7 +749,8 @@ int positive_have_wrcomb(void) ...@@ -716,7 +749,8 @@ int positive_have_wrcomb(void)
return 1; return 1;
} }
/* generic structure... /*
* Generic structure...
*/ */
struct mtrr_ops generic_mtrr_ops = { struct mtrr_ops generic_mtrr_ops = {
.use_intel_if = 1, .use_intel_if = 1,
......
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/uaccess.h> #include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/init.h>
#define LINE_SIZE 80 #define LINE_SIZE 80
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include "mtrr.h" #include "mtrr.h"
#define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private) #define FILE_FCOUNT(f) (((struct seq_file *)((f)->private_data))->private)
...@@ -35,8 +36,8 @@ static int ...@@ -35,8 +36,8 @@ static int
mtrr_file_add(unsigned long base, unsigned long size, mtrr_file_add(unsigned long base, unsigned long size,
unsigned int type, bool increment, struct file *file, int page) unsigned int type, bool increment, struct file *file, int page)
{ {
int reg, max;
unsigned int *fcount = FILE_FCOUNT(file); unsigned int *fcount = FILE_FCOUNT(file);
int reg, max;
max = num_var_ranges; max = num_var_ranges;
if (fcount == NULL) { if (fcount == NULL) {
...@@ -61,8 +62,8 @@ static int ...@@ -61,8 +62,8 @@ static int
mtrr_file_del(unsigned long base, unsigned long size, mtrr_file_del(unsigned long base, unsigned long size,
struct file *file, int page) struct file *file, int page)
{ {
int reg;
unsigned int *fcount = FILE_FCOUNT(file); unsigned int *fcount = FILE_FCOUNT(file);
int reg;
if (!page) { if (!page) {
if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)))
...@@ -81,13 +82,14 @@ mtrr_file_del(unsigned long base, unsigned long size, ...@@ -81,13 +82,14 @@ mtrr_file_del(unsigned long base, unsigned long size,
return reg; return reg;
} }
/* RED-PEN: seq_file can seek now. this is ignored. */ /*
* seq_file can seek but we ignore it.
*
* Format of control line:
* "base=%Lx size=%Lx type=%s" or "disable=%d"
*/
static ssize_t static ssize_t
mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos) mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
/* Format of control line:
"base=%Lx size=%Lx type=%s" OR:
"disable=%d"
*/
{ {
int i, err; int i, err;
unsigned long reg; unsigned long reg;
...@@ -100,15 +102,18 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos) ...@@ -100,15 +102,18 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
return -EPERM; return -EPERM;
if (!len) if (!len)
return -EINVAL; return -EINVAL;
memset(line, 0, LINE_SIZE); memset(line, 0, LINE_SIZE);
if (len > LINE_SIZE) if (len > LINE_SIZE)
len = LINE_SIZE; len = LINE_SIZE;
if (copy_from_user(line, buf, len - 1)) if (copy_from_user(line, buf, len - 1))
return -EFAULT; return -EFAULT;
linelen = strlen(line); linelen = strlen(line);
ptr = line + linelen - 1; ptr = line + linelen - 1;
if (linelen && *ptr == '\n') if (linelen && *ptr == '\n')
*ptr = '\0'; *ptr = '\0';
if (!strncmp(line, "disable=", 8)) { if (!strncmp(line, "disable=", 8)) {
reg = simple_strtoul(line + 8, &ptr, 0); reg = simple_strtoul(line + 8, &ptr, 0);
err = mtrr_del_page(reg, 0, 0); err = mtrr_del_page(reg, 0, 0);
...@@ -116,28 +121,35 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos) ...@@ -116,28 +121,35 @@ mtrr_write(struct file *file, const char __user *buf, size_t len, loff_t * ppos)
return err; return err;
return len; return len;
} }
if (strncmp(line, "base=", 5)) if (strncmp(line, "base=", 5))
return -EINVAL; return -EINVAL;
base = simple_strtoull(line + 5, &ptr, 0); base = simple_strtoull(line + 5, &ptr, 0);
for (; isspace(*ptr); ++ptr) ; for (; isspace(*ptr); ++ptr)
;
if (strncmp(ptr, "size=", 5)) if (strncmp(ptr, "size=", 5))
return -EINVAL; return -EINVAL;
size = simple_strtoull(ptr + 5, &ptr, 0); size = simple_strtoull(ptr + 5, &ptr, 0);
if ((base & 0xfff) || (size & 0xfff)) if ((base & 0xfff) || (size & 0xfff))
return -EINVAL; return -EINVAL;
for (; isspace(*ptr); ++ptr) ; for (; isspace(*ptr); ++ptr)
;
if (strncmp(ptr, "type=", 5)) if (strncmp(ptr, "type=", 5))
return -EINVAL; return -EINVAL;
ptr += 5; ptr += 5;
for (; isspace(*ptr); ++ptr) ; for (; isspace(*ptr); ++ptr)
;
for (i = 0; i < MTRR_NUM_TYPES; ++i) { for (i = 0; i < MTRR_NUM_TYPES; ++i) {
if (strcmp(ptr, mtrr_strings[i])) if (strcmp(ptr, mtrr_strings[i]))
continue; continue;
base >>= PAGE_SHIFT; base >>= PAGE_SHIFT;
size >>= PAGE_SHIFT; size >>= PAGE_SHIFT;
err = err = mtrr_add_page((unsigned long)base, (unsigned long)size, i, true);
mtrr_add_page((unsigned long) base, (unsigned long) size, i,
true);
if (err < 0) if (err < 0)
return err; return err;
return len; return len;
...@@ -181,7 +193,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) ...@@ -181,7 +193,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
case MTRRIOC32_SET_PAGE_ENTRY: case MTRRIOC32_SET_PAGE_ENTRY:
case MTRRIOC32_DEL_PAGE_ENTRY: case MTRRIOC32_DEL_PAGE_ENTRY:
case MTRRIOC32_KILL_PAGE_ENTRY: { case MTRRIOC32_KILL_PAGE_ENTRY: {
struct mtrr_sentry32 __user *s32 = (struct mtrr_sentry32 __user *)__arg; struct mtrr_sentry32 __user *s32;
s32 = (struct mtrr_sentry32 __user *)__arg;
err = get_user(sentry.base, &s32->base); err = get_user(sentry.base, &s32->base);
err |= get_user(sentry.size, &s32->size); err |= get_user(sentry.size, &s32->size);
err |= get_user(sentry.type, &s32->type); err |= get_user(sentry.type, &s32->type);
...@@ -191,7 +205,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) ...@@ -191,7 +205,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
} }
case MTRRIOC32_GET_ENTRY: case MTRRIOC32_GET_ENTRY:
case MTRRIOC32_GET_PAGE_ENTRY: { case MTRRIOC32_GET_PAGE_ENTRY: {
struct mtrr_gentry32 __user *g32 = (struct mtrr_gentry32 __user *)__arg; struct mtrr_gentry32 __user *g32;
g32 = (struct mtrr_gentry32 __user *)__arg;
err = get_user(gentry.regnum, &g32->regnum); err = get_user(gentry.regnum, &g32->regnum);
err |= get_user(gentry.base, &g32->base); err |= get_user(gentry.base, &g32->base);
err |= get_user(gentry.size, &g32->size); err |= get_user(gentry.size, &g32->size);
...@@ -314,7 +330,7 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) ...@@ -314,7 +330,7 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
if (err) if (err)
return err; return err;
switch(cmd) { switch (cmd) {
case MTRRIOC_GET_ENTRY: case MTRRIOC_GET_ENTRY:
case MTRRIOC_GET_PAGE_ENTRY: case MTRRIOC_GET_PAGE_ENTRY:
if (copy_to_user(arg, &gentry, sizeof gentry)) if (copy_to_user(arg, &gentry, sizeof gentry))
...@@ -323,7 +339,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) ...@@ -323,7 +339,9 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
case MTRRIOC32_GET_ENTRY: case MTRRIOC32_GET_ENTRY:
case MTRRIOC32_GET_PAGE_ENTRY: { case MTRRIOC32_GET_PAGE_ENTRY: {
struct mtrr_gentry32 __user *g32 = (struct mtrr_gentry32 __user *)__arg; struct mtrr_gentry32 __user *g32;
g32 = (struct mtrr_gentry32 __user *)__arg;
err = put_user(gentry.base, &g32->base); err = put_user(gentry.base, &g32->base);
err |= put_user(gentry.size, &g32->size); err |= put_user(gentry.size, &g32->size);
err |= put_user(gentry.regnum, &g32->regnum); err |= put_user(gentry.regnum, &g32->regnum);
...@@ -335,11 +353,10 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg) ...@@ -335,11 +353,10 @@ mtrr_ioctl(struct file *file, unsigned int cmd, unsigned long __arg)
return err; return err;
} }
static int static int mtrr_close(struct inode *ino, struct file *file)
mtrr_close(struct inode *ino, struct file *file)
{ {
int i, max;
unsigned int *fcount = FILE_FCOUNT(file); unsigned int *fcount = FILE_FCOUNT(file);
int i, max;
if (fcount != NULL) { if (fcount != NULL) {
max = num_var_ranges; max = num_var_ranges;
...@@ -388,9 +405,10 @@ static int mtrr_seq_show(struct seq_file *seq, void *offset) ...@@ -388,9 +405,10 @@ static int mtrr_seq_show(struct seq_file *seq, void *offset)
max = num_var_ranges; max = num_var_ranges;
for (i = 0; i < max; i++) { for (i = 0; i < max; i++) {
mtrr_if->get(i, &base, &size, &type); mtrr_if->get(i, &base, &size, &type);
if (size == 0) if (size == 0) {
mtrr_usage_table[i] = 0; mtrr_usage_table[i] = 0;
else { continue;
}
if (size < (0x100000 >> PAGE_SHIFT)) { if (size < (0x100000 >> PAGE_SHIFT)) {
/* less than 1MB */ /* less than 1MB */
factor = 'K'; factor = 'K';
...@@ -399,12 +417,12 @@ static int mtrr_seq_show(struct seq_file *seq, void *offset) ...@@ -399,12 +417,12 @@ static int mtrr_seq_show(struct seq_file *seq, void *offset)
factor = 'M'; factor = 'M';
size >>= 20 - PAGE_SHIFT; size >>= 20 - PAGE_SHIFT;
} }
/* RED-PEN: base can be > 32bit */ /* Base can be > 32bit */
len += seq_printf(seq, len += seq_printf(seq, "reg%02i: base=0x%06lx000 "
"reg%02i: base=0x%06lx000 (%5luMB), size=%5lu%cB, count=%d: %s\n", "(%5luMB), size=%5lu%cB, count=%d: %s\n",
i, base, base >> (20 - PAGE_SHIFT), size, factor, i, base, base >> (20 - PAGE_SHIFT), size,
mtrr_usage_table[i], mtrr_attrib_to_str(type)); factor, mtrr_usage_table[i],
} mtrr_attrib_to_str(type));
} }
return 0; return 0;
} }
...@@ -422,6 +440,5 @@ static int __init mtrr_if_init(void) ...@@ -422,6 +440,5 @@ static int __init mtrr_if_init(void)
proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops); proc_create("mtrr", S_IWUSR | S_IRUGO, NULL, &mtrr_fops);
return 0; return 0;
} }
arch_initcall(mtrr_if_init); arch_initcall(mtrr_if_init);
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
...@@ -31,37 +31,43 @@ ...@@ -31,37 +31,43 @@
System Programming Guide; Section 9.11. (1997 edition - PPro). System Programming Guide; Section 9.11. (1997 edition - PPro).
*/ */
#define DEBUG
#include <linux/types.h> /* FIXME: kvm_para.h needs this */
#include <linux/kvm_para.h>
#include <linux/uaccess.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/sort.h>
#include <linux/cpu.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/sort.h>
#include <asm/processor.h>
#include <asm/e820.h> #include <asm/e820.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/kvm_para.h>
#include "mtrr.h" #include "mtrr.h"
u32 num_var_ranges = 0; u32 num_var_ranges;
unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
static DEFINE_MUTEX(mtrr_mutex); static DEFINE_MUTEX(mtrr_mutex);
u64 size_or_mask, size_and_mask; u64 size_or_mask, size_and_mask;
static bool mtrr_aps_delayed_init;
static struct mtrr_ops * mtrr_ops[X86_VENDOR_NUM] = {}; static struct mtrr_ops *mtrr_ops[X86_VENDOR_NUM];
struct mtrr_ops * mtrr_if = NULL; struct mtrr_ops *mtrr_if;
static void set_mtrr(unsigned int reg, unsigned long base, static void set_mtrr(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type); unsigned long size, mtrr_type type);
void set_mtrr_ops(struct mtrr_ops * ops) void set_mtrr_ops(struct mtrr_ops *ops)
{ {
if (ops->vendor && ops->vendor < X86_VENDOR_NUM) if (ops->vendor && ops->vendor < X86_VENDOR_NUM)
mtrr_ops[ops->vendor] = ops; mtrr_ops[ops->vendor] = ops;
...@@ -73,29 +79,35 @@ static int have_wrcomb(void) ...@@ -73,29 +79,35 @@ static int have_wrcomb(void)
struct pci_dev *dev; struct pci_dev *dev;
u8 rev; u8 rev;
if ((dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, NULL);
/* ServerWorks LE chipsets < rev 6 have problems with write-combining if (dev != NULL) {
Don't allow it and leave room for other chipsets to be tagged */ /*
* ServerWorks LE chipsets < rev 6 have problems with
* write-combining. Don't allow it and leave room for other
* chipsets to be tagged
*/
if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS && if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
dev->device == PCI_DEVICE_ID_SERVERWORKS_LE) { dev->device == PCI_DEVICE_ID_SERVERWORKS_LE) {
pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev); pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev);
if (rev <= 5) { if (rev <= 5) {
printk(KERN_INFO "mtrr: Serverworks LE rev < 6 detected. Write-combining disabled.\n"); pr_info("mtrr: Serverworks LE rev < 6 detected. Write-combining disabled.\n");
pci_dev_put(dev); pci_dev_put(dev);
return 0; return 0;
} }
} }
/* Intel 450NX errata # 23. Non ascending cacheline evictions to /*
write combining memory may resulting in data corruption */ * Intel 450NX errata # 23. Non ascending cacheline evictions to
* write combining memory may resulting in data corruption
*/
if (dev->vendor == PCI_VENDOR_ID_INTEL && if (dev->vendor == PCI_VENDOR_ID_INTEL &&
dev->device == PCI_DEVICE_ID_INTEL_82451NX) { dev->device == PCI_DEVICE_ID_INTEL_82451NX) {
printk(KERN_INFO "mtrr: Intel 450NX MMC detected. Write-combining disabled.\n"); pr_info("mtrr: Intel 450NX MMC detected. Write-combining disabled.\n");
pci_dev_put(dev); pci_dev_put(dev);
return 0; return 0;
} }
pci_dev_put(dev); pci_dev_put(dev);
} }
return (mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0); return mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0;
} }
/* This function returns the number of variable MTRRs */ /* This function returns the number of variable MTRRs */
...@@ -103,12 +115,13 @@ static void __init set_num_var_ranges(void) ...@@ -103,12 +115,13 @@ static void __init set_num_var_ranges(void)
{ {
unsigned long config = 0, dummy; unsigned long config = 0, dummy;
if (use_intel()) { if (use_intel())
rdmsr(MSR_MTRRcap, config, dummy); rdmsr(MSR_MTRRcap, config, dummy);
} else if (is_cpu(AMD)) else if (is_cpu(AMD))
config = 2; config = 2;
else if (is_cpu(CYRIX) || is_cpu(CENTAUR)) else if (is_cpu(CYRIX) || is_cpu(CENTAUR))
config = 8; config = 8;
num_var_ranges = config & 0xff; num_var_ranges = config & 0xff;
} }
...@@ -130,10 +143,12 @@ struct set_mtrr_data { ...@@ -130,10 +143,12 @@ struct set_mtrr_data {
mtrr_type smp_type; mtrr_type smp_type;
}; };
/**
* ipi_handler - Synchronisation handler. Executed by "other" CPUs.
*
* Returns nothing.
*/
static void ipi_handler(void *info) static void ipi_handler(void *info)
/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs.
[RETURNS] Nothing.
*/
{ {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
struct set_mtrr_data *data = info; struct set_mtrr_data *data = info;
...@@ -142,18 +157,22 @@ static void ipi_handler(void *info) ...@@ -142,18 +157,22 @@ static void ipi_handler(void *info)
local_irq_save(flags); local_irq_save(flags);
atomic_dec(&data->count); atomic_dec(&data->count);
while(!atomic_read(&data->gate)) while (!atomic_read(&data->gate))
cpu_relax(); cpu_relax();
/* The master has cleared me to execute */ /* The master has cleared me to execute */
if (data->smp_reg != ~0U) if (data->smp_reg != ~0U) {
mtrr_if->set(data->smp_reg, data->smp_base, mtrr_if->set(data->smp_reg, data->smp_base,
data->smp_size, data->smp_type); data->smp_size, data->smp_type);
else } else if (mtrr_aps_delayed_init) {
/*
* Initialize the MTRRs inaddition to the synchronisation.
*/
mtrr_if->set_all(); mtrr_if->set_all();
}
atomic_dec(&data->count); atomic_dec(&data->count);
while(atomic_read(&data->gate)) while (atomic_read(&data->gate))
cpu_relax(); cpu_relax();
atomic_dec(&data->count); atomic_dec(&data->count);
...@@ -161,7 +180,8 @@ static void ipi_handler(void *info) ...@@ -161,7 +180,8 @@ static void ipi_handler(void *info)
#endif #endif
} }
static inline int types_compatible(mtrr_type type1, mtrr_type type2) { static inline int types_compatible(mtrr_type type1, mtrr_type type2)
{
return type1 == MTRR_TYPE_UNCACHABLE || return type1 == MTRR_TYPE_UNCACHABLE ||
type2 == MTRR_TYPE_UNCACHABLE || type2 == MTRR_TYPE_UNCACHABLE ||
(type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK) || (type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK) ||
...@@ -197,18 +217,19 @@ static inline int types_compatible(mtrr_type type1, mtrr_type type2) { ...@@ -197,18 +217,19 @@ static inline int types_compatible(mtrr_type type1, mtrr_type type2) {
* of CPUs. As each CPU disables interrupts, it'll decrement it once. We wait * of CPUs. As each CPU disables interrupts, it'll decrement it once. We wait
* until it hits 0 and proceed. We set the data.gate flag and reset data.count. * until it hits 0 and proceed. We set the data.gate flag and reset data.count.
* Meanwhile, they are waiting for that flag to be set. Once it's set, each * Meanwhile, they are waiting for that flag to be set. Once it's set, each
* CPU goes through the transition of updating MTRRs. The CPU vendors may each do it * CPU goes through the transition of updating MTRRs.
* differently, so we call mtrr_if->set() callback and let them take care of it. * The CPU vendors may each do it differently,
* When they're done, they again decrement data->count and wait for data.gate to * so we call mtrr_if->set() callback and let them take care of it.
* be reset. * When they're done, they again decrement data->count and wait for data.gate
* When we finish, we wait for data.count to hit 0 and toggle the data.gate flag. * to be reset.
* When we finish, we wait for data.count to hit 0 and toggle the data.gate flag
* Everyone then enables interrupts and we all continue on. * Everyone then enables interrupts and we all continue on.
* *
* Note that the mechanism is the same for UP systems, too; all the SMP stuff * Note that the mechanism is the same for UP systems, too; all the SMP stuff
* becomes nops. * becomes nops.
*/ */
static void set_mtrr(unsigned int reg, unsigned long base, static void
unsigned long size, mtrr_type type) set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type type)
{ {
struct set_mtrr_data data; struct set_mtrr_data data;
unsigned long flags; unsigned long flags;
...@@ -218,9 +239,10 @@ static void set_mtrr(unsigned int reg, unsigned long base, ...@@ -218,9 +239,10 @@ static void set_mtrr(unsigned int reg, unsigned long base,
data.smp_size = size; data.smp_size = size;
data.smp_type = type; data.smp_type = type;
atomic_set(&data.count, num_booting_cpus() - 1); atomic_set(&data.count, num_booting_cpus() - 1);
/* make sure data.count is visible before unleashing other CPUs */
/* Make sure data.count is visible before unleashing other CPUs */
smp_wmb(); smp_wmb();
atomic_set(&data.gate,0); atomic_set(&data.gate, 0);
/* Start the ball rolling on other CPUs */ /* Start the ball rolling on other CPUs */
if (smp_call_function(ipi_handler, &data, 0) != 0) if (smp_call_function(ipi_handler, &data, 0) != 0)
...@@ -228,38 +250,41 @@ static void set_mtrr(unsigned int reg, unsigned long base, ...@@ -228,38 +250,41 @@ static void set_mtrr(unsigned int reg, unsigned long base,
local_irq_save(flags); local_irq_save(flags);
while(atomic_read(&data.count)) while (atomic_read(&data.count))
cpu_relax(); cpu_relax();
/* ok, reset count and toggle gate */ /* Ok, reset count and toggle gate */
atomic_set(&data.count, num_booting_cpus() - 1); atomic_set(&data.count, num_booting_cpus() - 1);
smp_wmb(); smp_wmb();
atomic_set(&data.gate,1); atomic_set(&data.gate, 1);
/* do our MTRR business */ /* Do our MTRR business */
/* HACK! /*
* HACK!
* We use this same function to initialize the mtrrs on boot. * We use this same function to initialize the mtrrs on boot.
* The state of the boot cpu's mtrrs has been saved, and we want * The state of the boot cpu's mtrrs has been saved, and we want
* to replicate across all the APs. * to replicate across all the APs.
* If we're doing that @reg is set to something special... * If we're doing that @reg is set to something special...
*/ */
if (reg != ~0U) if (reg != ~0U)
mtrr_if->set(reg,base,size,type); mtrr_if->set(reg, base, size, type);
else if (!mtrr_aps_delayed_init)
mtrr_if->set_all();
/* wait for the others */ /* Wait for the others */
while(atomic_read(&data.count)) while (atomic_read(&data.count))
cpu_relax(); cpu_relax();
atomic_set(&data.count, num_booting_cpus() - 1); atomic_set(&data.count, num_booting_cpus() - 1);
smp_wmb(); smp_wmb();
atomic_set(&data.gate,0); atomic_set(&data.gate, 0);
/* /*
* Wait here for everyone to have seen the gate change * Wait here for everyone to have seen the gate change
* So we're the last ones to touch 'data' * So we're the last ones to touch 'data'
*/ */
while(atomic_read(&data.count)) while (atomic_read(&data.count))
cpu_relax(); cpu_relax();
local_irq_restore(flags); local_irq_restore(flags);
...@@ -300,39 +325,38 @@ static void set_mtrr(unsigned int reg, unsigned long base, ...@@ -300,39 +325,38 @@ static void set_mtrr(unsigned int reg, unsigned long base,
* BUGS: Needs a quiet flag for the cases where drivers do not mind * BUGS: Needs a quiet flag for the cases where drivers do not mind
* failures and do not wish system log messages to be sent. * failures and do not wish system log messages to be sent.
*/ */
int mtrr_add_page(unsigned long base, unsigned long size, int mtrr_add_page(unsigned long base, unsigned long size,
unsigned int type, bool increment) unsigned int type, bool increment)
{ {
unsigned long lbase, lsize;
int i, replace, error; int i, replace, error;
mtrr_type ltype; mtrr_type ltype;
unsigned long lbase, lsize;
if (!mtrr_if) if (!mtrr_if)
return -ENXIO; return -ENXIO;
if ((error = mtrr_if->validate_add_page(base,size,type))) error = mtrr_if->validate_add_page(base, size, type);
if (error)
return error; return error;
if (type >= MTRR_NUM_TYPES) { if (type >= MTRR_NUM_TYPES) {
printk(KERN_WARNING "mtrr: type: %u invalid\n", type); pr_warning("mtrr: type: %u invalid\n", type);
return -EINVAL; return -EINVAL;
} }
/* If the type is WC, check that this processor supports it */ /* If the type is WC, check that this processor supports it */
if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb()) { if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb()) {
printk(KERN_WARNING pr_warning("mtrr: your processor doesn't support write-combining\n");
"mtrr: your processor doesn't support write-combining\n");
return -ENOSYS; return -ENOSYS;
} }
if (!size) { if (!size) {
printk(KERN_WARNING "mtrr: zero sized request\n"); pr_warning("mtrr: zero sized request\n");
return -EINVAL; return -EINVAL;
} }
if (base & size_or_mask || size & size_or_mask) { if (base & size_or_mask || size & size_or_mask) {
printk(KERN_WARNING "mtrr: base or size exceeds the MTRR width\n"); pr_warning("mtrr: base or size exceeds the MTRR width\n");
return -EINVAL; return -EINVAL;
} }
...@@ -341,25 +365,29 @@ int mtrr_add_page(unsigned long base, unsigned long size, ...@@ -341,25 +365,29 @@ int mtrr_add_page(unsigned long base, unsigned long size,
/* No CPU hotplug when we change MTRR entries */ /* No CPU hotplug when we change MTRR entries */
get_online_cpus(); get_online_cpus();
/* Search for existing MTRR */ /* Search for existing MTRR */
mutex_lock(&mtrr_mutex); mutex_lock(&mtrr_mutex);
for (i = 0; i < num_var_ranges; ++i) { for (i = 0; i < num_var_ranges; ++i) {
mtrr_if->get(i, &lbase, &lsize, &ltype); mtrr_if->get(i, &lbase, &lsize, &ltype);
if (!lsize || base > lbase + lsize - 1 || base + size - 1 < lbase) if (!lsize || base > lbase + lsize - 1 ||
base + size - 1 < lbase)
continue; continue;
/* At this point we know there is some kind of overlap/enclosure */ /*
* At this point we know there is some kind of
* overlap/enclosure
*/
if (base < lbase || base + size - 1 > lbase + lsize - 1) { if (base < lbase || base + size - 1 > lbase + lsize - 1) {
if (base <= lbase && base + size - 1 >= lbase + lsize - 1) { if (base <= lbase &&
base + size - 1 >= lbase + lsize - 1) {
/* New region encloses an existing region */ /* New region encloses an existing region */
if (type == ltype) { if (type == ltype) {
replace = replace == -1 ? i : -2; replace = replace == -1 ? i : -2;
continue; continue;
} } else if (types_compatible(type, ltype))
else if (types_compatible(type, ltype))
continue; continue;
} }
printk(KERN_WARNING pr_warning("mtrr: 0x%lx000,0x%lx000 overlaps existing"
"mtrr: 0x%lx000,0x%lx000 overlaps existing"
" 0x%lx000,0x%lx000\n", base, size, lbase, " 0x%lx000,0x%lx000\n", base, size, lbase,
lsize); lsize);
goto out; goto out;
...@@ -368,7 +396,7 @@ int mtrr_add_page(unsigned long base, unsigned long size, ...@@ -368,7 +396,7 @@ int mtrr_add_page(unsigned long base, unsigned long size,
if (ltype != type) { if (ltype != type) {
if (types_compatible(type, ltype)) if (types_compatible(type, ltype))
continue; continue;
printk (KERN_WARNING "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", pr_warning("mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n",
base, size, mtrr_attrib_to_str(ltype), base, size, mtrr_attrib_to_str(ltype),
mtrr_attrib_to_str(type)); mtrr_attrib_to_str(type));
goto out; goto out;
...@@ -393,8 +421,9 @@ int mtrr_add_page(unsigned long base, unsigned long size, ...@@ -393,8 +421,9 @@ int mtrr_add_page(unsigned long base, unsigned long size,
mtrr_usage_table[replace] = 0; mtrr_usage_table[replace] = 0;
} }
} }
} else } else {
printk(KERN_INFO "mtrr: no more MTRRs available\n"); pr_info("mtrr: no more MTRRs available\n");
}
error = i; error = i;
out: out:
mutex_unlock(&mtrr_mutex); mutex_unlock(&mtrr_mutex);
...@@ -405,10 +434,8 @@ int mtrr_add_page(unsigned long base, unsigned long size, ...@@ -405,10 +434,8 @@ int mtrr_add_page(unsigned long base, unsigned long size,
static int mtrr_check(unsigned long base, unsigned long size) static int mtrr_check(unsigned long base, unsigned long size)
{ {
if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
printk(KERN_WARNING pr_warning("mtrr: size and base must be multiples of 4 kiB\n");
"mtrr: size and base must be multiples of 4 kiB\n"); pr_debug("mtrr: size: 0x%lx base: 0x%lx\n", size, base);
printk(KERN_DEBUG
"mtrr: size: 0x%lx base: 0x%lx\n", size, base);
dump_stack(); dump_stack();
return -1; return -1;
} }
...@@ -450,9 +477,7 @@ static int mtrr_check(unsigned long base, unsigned long size) ...@@ -450,9 +477,7 @@ static int mtrr_check(unsigned long base, unsigned long size)
* BUGS: Needs a quiet flag for the cases where drivers do not mind * BUGS: Needs a quiet flag for the cases where drivers do not mind
* failures and do not wish system log messages to be sent. * failures and do not wish system log messages to be sent.
*/ */
int mtrr_add(unsigned long base, unsigned long size, unsigned int type,
int
mtrr_add(unsigned long base, unsigned long size, unsigned int type,
bool increment) bool increment)
{ {
if (mtrr_check(base, size)) if (mtrr_check(base, size))
...@@ -460,6 +485,7 @@ mtrr_add(unsigned long base, unsigned long size, unsigned int type, ...@@ -460,6 +485,7 @@ mtrr_add(unsigned long base, unsigned long size, unsigned int type,
return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
increment); increment);
} }
EXPORT_SYMBOL(mtrr_add);
/** /**
* mtrr_del_page - delete a memory type region * mtrr_del_page - delete a memory type region
...@@ -475,7 +501,6 @@ mtrr_add(unsigned long base, unsigned long size, unsigned int type, ...@@ -475,7 +501,6 @@ mtrr_add(unsigned long base, unsigned long size, unsigned int type,
* On success the register is returned, on failure a negative error * On success the register is returned, on failure a negative error
* code. * code.
*/ */
int mtrr_del_page(int reg, unsigned long base, unsigned long size) int mtrr_del_page(int reg, unsigned long base, unsigned long size)
{ {
int i, max; int i, max;
...@@ -500,22 +525,22 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size) ...@@ -500,22 +525,22 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
} }
} }
if (reg < 0) { if (reg < 0) {
printk(KERN_DEBUG "mtrr: no MTRR for %lx000,%lx000 found\n", base, pr_debug("mtrr: no MTRR for %lx000,%lx000 found\n",
size); base, size);
goto out; goto out;
} }
} }
if (reg >= max) { if (reg >= max) {
printk(KERN_WARNING "mtrr: register: %d too big\n", reg); pr_warning("mtrr: register: %d too big\n", reg);
goto out; goto out;
} }
mtrr_if->get(reg, &lbase, &lsize, &ltype); mtrr_if->get(reg, &lbase, &lsize, &ltype);
if (lsize < 1) { if (lsize < 1) {
printk(KERN_WARNING "mtrr: MTRR %d not used\n", reg); pr_warning("mtrr: MTRR %d not used\n", reg);
goto out; goto out;
} }
if (mtrr_usage_table[reg] < 1) { if (mtrr_usage_table[reg] < 1) {
printk(KERN_WARNING "mtrr: reg: %d has count=0\n", reg); pr_warning("mtrr: reg: %d has count=0\n", reg);
goto out; goto out;
} }
if (--mtrr_usage_table[reg] < 1) if (--mtrr_usage_table[reg] < 1)
...@@ -526,6 +551,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size) ...@@ -526,6 +551,7 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
put_online_cpus(); put_online_cpus();
return error; return error;
} }
/** /**
* mtrr_del - delete a memory type region * mtrr_del - delete a memory type region
* @reg: Register returned by mtrr_add * @reg: Register returned by mtrr_add
...@@ -540,19 +566,16 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size) ...@@ -540,19 +566,16 @@ int mtrr_del_page(int reg, unsigned long base, unsigned long size)
* On success the register is returned, on failure a negative error * On success the register is returned, on failure a negative error
* code. * code.
*/ */
int mtrr_del(int reg, unsigned long base, unsigned long size)
int
mtrr_del(int reg, unsigned long base, unsigned long size)
{ {
if (mtrr_check(base, size)) if (mtrr_check(base, size))
return -EINVAL; return -EINVAL;
return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
} }
EXPORT_SYMBOL(mtrr_add);
EXPORT_SYMBOL(mtrr_del); EXPORT_SYMBOL(mtrr_del);
/* HACK ALERT! /*
* HACK ALERT!
* These should be called implicitly, but we can't yet until all the initcall * These should be called implicitly, but we can't yet until all the initcall
* stuff is done... * stuff is done...
*/ */
...@@ -576,30 +599,29 @@ struct mtrr_value { ...@@ -576,30 +599,29 @@ struct mtrr_value {
static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES]; static struct mtrr_value mtrr_value[MTRR_MAX_VAR_RANGES];
static int mtrr_save(struct sys_device * sysdev, pm_message_t state) static int mtrr_save(struct sys_device *sysdev, pm_message_t state)
{ {
int i; int i;
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
mtrr_if->get(i, mtrr_if->get(i, &mtrr_value[i].lbase,
&mtrr_value[i].lbase,
&mtrr_value[i].lsize, &mtrr_value[i].lsize,
&mtrr_value[i].ltype); &mtrr_value[i].ltype);
} }
return 0; return 0;
} }
static int mtrr_restore(struct sys_device * sysdev) static int mtrr_restore(struct sys_device *sysdev)
{ {
int i; int i;
for (i = 0; i < num_var_ranges; i++) { for (i = 0; i < num_var_ranges; i++) {
if (mtrr_value[i].lsize) if (mtrr_value[i].lsize) {
set_mtrr(i, set_mtrr(i, mtrr_value[i].lbase,
mtrr_value[i].lbase,
mtrr_value[i].lsize, mtrr_value[i].lsize,
mtrr_value[i].ltype); mtrr_value[i].ltype);
} }
}
return 0; return 0;
} }
...@@ -622,6 +644,7 @@ int __initdata changed_by_mtrr_cleanup; ...@@ -622,6 +644,7 @@ int __initdata changed_by_mtrr_cleanup;
void __init mtrr_bp_init(void) void __init mtrr_bp_init(void)
{ {
u32 phys_addr; u32 phys_addr;
init_ifs(); init_ifs();
phys_addr = 32; phys_addr = 32;
...@@ -632,9 +655,11 @@ void __init mtrr_bp_init(void) ...@@ -632,9 +655,11 @@ void __init mtrr_bp_init(void)
size_and_mask = 0x00f00000; size_and_mask = 0x00f00000;
phys_addr = 36; phys_addr = 36;
/* This is an AMD specific MSR, but we assume(hope?) that /*
Intel will implement it to when they extend the address * This is an AMD specific MSR, but we assume(hope?) that
bus of the Xeon. */ * Intel will implement it to when they extend the address
* bus of the Xeon.
*/
if (cpuid_eax(0x80000000) >= 0x80000008) { if (cpuid_eax(0x80000000) >= 0x80000008) {
phys_addr = cpuid_eax(0x80000008) & 0xff; phys_addr = cpuid_eax(0x80000008) & 0xff;
/* CPUID workaround for Intel 0F33/0F34 CPU */ /* CPUID workaround for Intel 0F33/0F34 CPU */
...@@ -649,8 +674,10 @@ void __init mtrr_bp_init(void) ...@@ -649,8 +674,10 @@ void __init mtrr_bp_init(void)
size_and_mask = ~size_or_mask & 0xfffff00000ULL; size_and_mask = ~size_or_mask & 0xfffff00000ULL;
} else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR && } else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
boot_cpu_data.x86 == 6) { boot_cpu_data.x86 == 6) {
/* VIA C* family have Intel style MTRRs, but /*
don't support PAE */ * VIA C* family have Intel style MTRRs,
* but don't support PAE
*/
size_or_mask = 0xfff00000; /* 32 bits */ size_or_mask = 0xfff00000; /* 32 bits */
size_and_mask = 0; size_and_mask = 0;
phys_addr = 32; phys_addr = 32;
...@@ -694,30 +721,28 @@ void __init mtrr_bp_init(void) ...@@ -694,30 +721,28 @@ void __init mtrr_bp_init(void)
changed_by_mtrr_cleanup = 1; changed_by_mtrr_cleanup = 1;
mtrr_if->set_all(); mtrr_if->set_all();
} }
} }
} }
} }
void mtrr_ap_init(void) void mtrr_ap_init(void)
{ {
unsigned long flags; if (!use_intel() || mtrr_aps_delayed_init)
if (!mtrr_if || !use_intel())
return; return;
/* /*
* Ideally we should hold mtrr_mutex here to avoid mtrr entries changed, * Ideally we should hold mtrr_mutex here to avoid mtrr entries
* but this routine will be called in cpu boot time, holding the lock * changed, but this routine will be called in cpu boot time,
* breaks it. This routine is called in two cases: 1.very earily time * holding the lock breaks it.
* of software resume, when there absolutely isn't mtrr entry changes; *
* 2.cpu hotadd time. We let mtrr_add/del_page hold cpuhotplug lock to * This routine is called in two cases:
* prevent mtrr entry changes *
* 1. very earily time of software resume, when there absolutely
* isn't mtrr entry changes;
*
* 2. cpu hotadd time. We let mtrr_add/del_page hold cpuhotplug
* lock to prevent mtrr entry changes
*/ */
local_irq_save(flags); set_mtrr(~0U, 0, 0, 0);
mtrr_if->set_all();
local_irq_restore(flags);
} }
/** /**
...@@ -728,23 +753,55 @@ void mtrr_save_state(void) ...@@ -728,23 +753,55 @@ void mtrr_save_state(void)
smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1); smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1);
} }
void set_mtrr_aps_delayed_init(void)
{
if (!use_intel())
return;
mtrr_aps_delayed_init = true;
}
/*
* MTRR initialization for all AP's
*/
void mtrr_aps_init(void)
{
if (!use_intel())
return;
set_mtrr(~0U, 0, 0, 0);
mtrr_aps_delayed_init = false;
}
void mtrr_bp_restore(void)
{
if (!use_intel())
return;
mtrr_if->set_all();
}
static int __init mtrr_init_finialize(void) static int __init mtrr_init_finialize(void)
{ {
if (!mtrr_if) if (!mtrr_if)
return 0; return 0;
if (use_intel()) { if (use_intel()) {
if (!changed_by_mtrr_cleanup) if (!changed_by_mtrr_cleanup)
mtrr_state_warn(); mtrr_state_warn();
} else { return 0;
/* The CPUs haven't MTRR and seem to not support SMP. They have }
/*
* The CPU has no MTRR and seems to not support SMP. They have
* specific drivers, we use a tricky method to support * specific drivers, we use a tricky method to support
* suspend/resume for them. * suspend/resume for them.
*
* TBD: is there any system with such CPU which supports * TBD: is there any system with such CPU which supports
* suspend/resume? if no, we should remove the code. * suspend/resume? If no, we should remove the code.
*/ */
sysdev_driver_register(&cpu_sysdev_class, sysdev_driver_register(&cpu_sysdev_class, &mtrr_sysdev_driver);
&mtrr_sysdev_driver);
}
return 0; return 0;
} }
subsys_initcall(mtrr_init_finialize); subsys_initcall(mtrr_init_finialize);
/* /*
* local mtrr defines. * local MTRR defines.
*/ */
#include <linux/types.h> #include <linux/types.h>
...@@ -14,13 +14,12 @@ extern unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; ...@@ -14,13 +14,12 @@ extern unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES];
struct mtrr_ops { struct mtrr_ops {
u32 vendor; u32 vendor;
u32 use_intel_if; u32 use_intel_if;
// void (*init)(void);
void (*set)(unsigned int reg, unsigned long base, void (*set)(unsigned int reg, unsigned long base,
unsigned long size, mtrr_type type); unsigned long size, mtrr_type type);
void (*set_all)(void); void (*set_all)(void);
void (*get)(unsigned int reg, unsigned long *base, void (*get)(unsigned int reg, unsigned long *base,
unsigned long *size, mtrr_type * type); unsigned long *size, mtrr_type *type);
int (*get_free_region)(unsigned long base, unsigned long size, int (*get_free_region)(unsigned long base, unsigned long size,
int replace_reg); int replace_reg);
int (*validate_add_page)(unsigned long base, unsigned long size, int (*validate_add_page)(unsigned long base, unsigned long size,
...@@ -54,10 +53,10 @@ void fill_mtrr_var_range(unsigned int index, ...@@ -54,10 +53,10 @@ void fill_mtrr_var_range(unsigned int index,
u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi); u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi);
void get_mtrr_state(void); void get_mtrr_state(void);
extern void set_mtrr_ops(struct mtrr_ops * ops); extern void set_mtrr_ops(struct mtrr_ops *ops);
extern u64 size_or_mask, size_and_mask; extern u64 size_or_mask, size_and_mask;
extern struct mtrr_ops * mtrr_if; extern struct mtrr_ops *mtrr_if;
#define is_cpu(vnd) (mtrr_if && mtrr_if->vendor == X86_VENDOR_##vnd) #define is_cpu(vnd) (mtrr_if && mtrr_if->vendor == X86_VENDOR_##vnd)
#define use_intel() (mtrr_if && mtrr_if->use_intel_if == 1) #define use_intel() (mtrr_if && mtrr_if->use_intel_if == 1)
......
#include <linux/mm.h>
#include <linux/init.h> #include <linux/init.h>
#include <asm/io.h> #include <linux/io.h>
#include <asm/mtrr.h> #include <linux/mm.h>
#include <asm/msr.h>
#include <asm/processor-cyrix.h> #include <asm/processor-cyrix.h>
#include <asm/processor-flags.h> #include <asm/processor-flags.h>
#include "mtrr.h" #include <asm/mtrr.h>
#include <asm/msr.h>
#include "mtrr.h"
/* Put the processor into a state where MTRRs can be safely set */ /* Put the processor into a state where MTRRs can be safely set */
void set_mtrr_prepare_save(struct set_mtrr_context *ctxt) void set_mtrr_prepare_save(struct set_mtrr_context *ctxt)
...@@ -33,24 +34,31 @@ void set_mtrr_prepare_save(struct set_mtrr_context *ctxt) ...@@ -33,24 +34,31 @@ void set_mtrr_prepare_save(struct set_mtrr_context *ctxt)
write_cr0(cr0); write_cr0(cr0);
wbinvd(); wbinvd();
if (use_intel()) if (use_intel()) {
/* Save MTRR state */ /* Save MTRR state */
rdmsr(MSR_MTRRdefType, ctxt->deftype_lo, ctxt->deftype_hi); rdmsr(MSR_MTRRdefType, ctxt->deftype_lo, ctxt->deftype_hi);
else } else {
/* Cyrix ARRs - everything else were excluded at the top */ /*
* Cyrix ARRs -
* everything else were excluded at the top
*/
ctxt->ccr3 = getCx86(CX86_CCR3); ctxt->ccr3 = getCx86(CX86_CCR3);
} }
}
} }
void set_mtrr_cache_disable(struct set_mtrr_context *ctxt) void set_mtrr_cache_disable(struct set_mtrr_context *ctxt)
{ {
if (use_intel()) if (use_intel()) {
/* Disable MTRRs, and set the default type to uncached */ /* Disable MTRRs, and set the default type to uncached */
mtrr_wrmsr(MSR_MTRRdefType, ctxt->deftype_lo & 0xf300UL, mtrr_wrmsr(MSR_MTRRdefType, ctxt->deftype_lo & 0xf300UL,
ctxt->deftype_hi); ctxt->deftype_hi);
else if (is_cpu(CYRIX)) } else {
if (is_cpu(CYRIX)) {
/* Cyrix ARRs - everything else were excluded at the top */ /* Cyrix ARRs - everything else were excluded at the top */
setCx86(CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10); setCx86(CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10);
}
}
} }
/* Restore the processor after a set_mtrr_prepare */ /* Restore the processor after a set_mtrr_prepare */
...@@ -62,12 +70,17 @@ void set_mtrr_done(struct set_mtrr_context *ctxt) ...@@ -62,12 +70,17 @@ void set_mtrr_done(struct set_mtrr_context *ctxt)
wbinvd(); wbinvd();
/* Restore MTRRdefType */ /* Restore MTRRdefType */
if (use_intel()) if (use_intel()) {
/* Intel (P6) standard MTRRs */ /* Intel (P6) standard MTRRs */
mtrr_wrmsr(MSR_MTRRdefType, ctxt->deftype_lo, ctxt->deftype_hi); mtrr_wrmsr(MSR_MTRRdefType, ctxt->deftype_lo,
else ctxt->deftype_hi);
/* Cyrix ARRs - everything else was excluded at the top */ } else {
/*
* Cyrix ARRs -
* everything else was excluded at the top
*/
setCx86(CX86_CCR3, ctxt->ccr3); setCx86(CX86_CCR3, ctxt->ccr3);
}
/* Enable caches */ /* Enable caches */
write_cr0(read_cr0() & 0xbfffffff); write_cr0(read_cr0() & 0xbfffffff);
...@@ -79,4 +92,3 @@ void set_mtrr_done(struct set_mtrr_context *ctxt) ...@@ -79,4 +92,3 @@ void set_mtrr_done(struct set_mtrr_context *ctxt)
/* Re-enable interrupts locally (if enabled previously) */ /* Re-enable interrupts locally (if enabled previously) */
local_irq_restore(ctxt->flags); local_irq_restore(ctxt->flags);
} }
...@@ -68,16 +68,16 @@ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr) ...@@ -68,16 +68,16 @@ static inline unsigned int nmi_perfctr_msr_to_bit(unsigned int msr)
/* returns the bit offset of the performance counter register */ /* returns the bit offset of the performance counter register */
switch (boot_cpu_data.x86_vendor) { switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD: case X86_VENDOR_AMD:
return (msr - MSR_K7_PERFCTR0); return msr - MSR_K7_PERFCTR0;
case X86_VENDOR_INTEL: case X86_VENDOR_INTEL:
if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
return (msr - MSR_ARCH_PERFMON_PERFCTR0); return msr - MSR_ARCH_PERFMON_PERFCTR0;
switch (boot_cpu_data.x86) { switch (boot_cpu_data.x86) {
case 6: case 6:
return (msr - MSR_P6_PERFCTR0); return msr - MSR_P6_PERFCTR0;
case 15: case 15:
return (msr - MSR_P4_BPU_PERFCTR0); return msr - MSR_P4_BPU_PERFCTR0;
} }
} }
return 0; return 0;
...@@ -92,16 +92,16 @@ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr) ...@@ -92,16 +92,16 @@ static inline unsigned int nmi_evntsel_msr_to_bit(unsigned int msr)
/* returns the bit offset of the event selection register */ /* returns the bit offset of the event selection register */
switch (boot_cpu_data.x86_vendor) { switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_AMD: case X86_VENDOR_AMD:
return (msr - MSR_K7_EVNTSEL0); return msr - MSR_K7_EVNTSEL0;
case X86_VENDOR_INTEL: case X86_VENDOR_INTEL:
if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) if (cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON))
return (msr - MSR_ARCH_PERFMON_EVENTSEL0); return msr - MSR_ARCH_PERFMON_EVENTSEL0;
switch (boot_cpu_data.x86) { switch (boot_cpu_data.x86) {
case 6: case 6:
return (msr - MSR_P6_EVNTSEL0); return msr - MSR_P6_EVNTSEL0;
case 15: case 15:
return (msr - MSR_P4_BSU_ESCR0); return msr - MSR_P4_BSU_ESCR0;
} }
} }
return 0; return 0;
...@@ -113,7 +113,7 @@ int avail_to_resrv_perfctr_nmi_bit(unsigned int counter) ...@@ -113,7 +113,7 @@ int avail_to_resrv_perfctr_nmi_bit(unsigned int counter)
{ {
BUG_ON(counter > NMI_MAX_COUNTER_BITS); BUG_ON(counter > NMI_MAX_COUNTER_BITS);
return (!test_bit(counter, perfctr_nmi_owner)); return !test_bit(counter, perfctr_nmi_owner);
} }
/* checks the an msr for availability */ /* checks the an msr for availability */
...@@ -124,7 +124,7 @@ int avail_to_resrv_perfctr_nmi(unsigned int msr) ...@@ -124,7 +124,7 @@ int avail_to_resrv_perfctr_nmi(unsigned int msr)
counter = nmi_perfctr_msr_to_bit(msr); counter = nmi_perfctr_msr_to_bit(msr);
BUG_ON(counter > NMI_MAX_COUNTER_BITS); BUG_ON(counter > NMI_MAX_COUNTER_BITS);
return (!test_bit(counter, perfctr_nmi_owner)); return !test_bit(counter, perfctr_nmi_owner);
} }
EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi_bit); EXPORT_SYMBOL(avail_to_resrv_perfctr_nmi_bit);
...@@ -251,7 +251,7 @@ static void write_watchdog_counter(unsigned int perfctr_msr, ...@@ -251,7 +251,7 @@ static void write_watchdog_counter(unsigned int perfctr_msr,
u64 count = (u64)cpu_khz * 1000; u64 count = (u64)cpu_khz * 1000;
do_div(count, nmi_hz); do_div(count, nmi_hz);
if(descr) if (descr)
pr_debug("setting %s to -0x%08Lx\n", descr, count); pr_debug("setting %s to -0x%08Lx\n", descr, count);
wrmsrl(perfctr_msr, 0 - count); wrmsrl(perfctr_msr, 0 - count);
} }
...@@ -262,7 +262,7 @@ static void write_watchdog_counter32(unsigned int perfctr_msr, ...@@ -262,7 +262,7 @@ static void write_watchdog_counter32(unsigned int perfctr_msr,
u64 count = (u64)cpu_khz * 1000; u64 count = (u64)cpu_khz * 1000;
do_div(count, nmi_hz); do_div(count, nmi_hz);
if(descr) if (descr)
pr_debug("setting %s to -0x%08Lx\n", descr, count); pr_debug("setting %s to -0x%08Lx\n", descr, count);
wrmsr(perfctr_msr, (u32)(-count), 0); wrmsr(perfctr_msr, (u32)(-count), 0);
} }
...@@ -296,7 +296,7 @@ static int setup_k7_watchdog(unsigned nmi_hz) ...@@ -296,7 +296,7 @@ static int setup_k7_watchdog(unsigned nmi_hz)
/* setup the timer */ /* setup the timer */
wrmsr(evntsel_msr, evntsel, 0); wrmsr(evntsel_msr, evntsel, 0);
write_watchdog_counter(perfctr_msr, "K7_PERFCTR0",nmi_hz); write_watchdog_counter(perfctr_msr, "K7_PERFCTR0", nmi_hz);
/* initialize the wd struct before enabling */ /* initialize the wd struct before enabling */
wd->perfctr_msr = perfctr_msr; wd->perfctr_msr = perfctr_msr;
...@@ -387,7 +387,7 @@ static int setup_p6_watchdog(unsigned nmi_hz) ...@@ -387,7 +387,7 @@ static int setup_p6_watchdog(unsigned nmi_hz)
/* setup the timer */ /* setup the timer */
wrmsr(evntsel_msr, evntsel, 0); wrmsr(evntsel_msr, evntsel, 0);
nmi_hz = adjust_for_32bit_ctr(nmi_hz); nmi_hz = adjust_for_32bit_ctr(nmi_hz);
write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0",nmi_hz); write_watchdog_counter32(perfctr_msr, "P6_PERFCTR0", nmi_hz);
/* initialize the wd struct before enabling */ /* initialize the wd struct before enabling */
wd->perfctr_msr = perfctr_msr; wd->perfctr_msr = perfctr_msr;
...@@ -415,7 +415,7 @@ static void __kprobes p6_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz) ...@@ -415,7 +415,7 @@ static void __kprobes p6_rearm(struct nmi_watchdog_ctlblk *wd, unsigned nmi_hz)
apic_write(APIC_LVTPC, APIC_DM_NMI); apic_write(APIC_LVTPC, APIC_DM_NMI);
/* P6/ARCH_PERFMON has 32 bit counter write */ /* P6/ARCH_PERFMON has 32 bit counter write */
write_watchdog_counter32(wd->perfctr_msr, NULL,nmi_hz); write_watchdog_counter32(wd->perfctr_msr, NULL, nmi_hz);
} }
static const struct wd_ops p6_wd_ops = { static const struct wd_ops p6_wd_ops = {
...@@ -662,7 +662,8 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz) ...@@ -662,7 +662,8 @@ static int setup_intel_arch_watchdog(unsigned nmi_hz)
* NOTE: Corresponding bit = 0 in ebx indicates event present. * NOTE: Corresponding bit = 0 in ebx indicates event present.
*/ */
cpuid(10, &(eax.full), &ebx, &unused, &unused); cpuid(10, &(eax.full), &ebx, &unused, &unused);
if ((eax.split.mask_length < (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX+1)) || if ((eax.split.mask_length <
(ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX+1)) ||
(ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT)) (ebx & ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT))
return 0; return 0;
......
...@@ -128,7 +128,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) ...@@ -128,7 +128,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
if (i < ARRAY_SIZE(x86_power_flags) && if (i < ARRAY_SIZE(x86_power_flags) &&
x86_power_flags[i]) x86_power_flags[i])
seq_printf(m, "%s%s", seq_printf(m, "%s%s",
x86_power_flags[i][0]?" ":"", x86_power_flags[i][0] ? " " : "",
x86_power_flags[i]); x86_power_flags[i]);
else else
seq_printf(m, " [%d]", i); seq_printf(m, " [%d]", i);
......
...@@ -509,15 +509,15 @@ enum bts_field { ...@@ -509,15 +509,15 @@ enum bts_field {
bts_escape = ((unsigned long)-1 & ~bts_qual_mask) bts_escape = ((unsigned long)-1 & ~bts_qual_mask)
}; };
static inline unsigned long bts_get(const char *base, enum bts_field field) static inline unsigned long bts_get(const char *base, unsigned long field)
{ {
base += (ds_cfg.sizeof_ptr_field * field); base += (ds_cfg.sizeof_ptr_field * field);
return *(unsigned long *)base; return *(unsigned long *)base;
} }
static inline void bts_set(char *base, enum bts_field field, unsigned long val) static inline void bts_set(char *base, unsigned long field, unsigned long val)
{ {
base += (ds_cfg.sizeof_ptr_field * field);; base += (ds_cfg.sizeof_ptr_field * field);
(*(unsigned long *)base) = val; (*(unsigned long *)base) = val;
} }
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/ftrace.h>
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
......
...@@ -218,7 +218,6 @@ bool handle_irq(unsigned irq, struct pt_regs *regs) ...@@ -218,7 +218,6 @@ bool handle_irq(unsigned irq, struct pt_regs *regs)
void fixup_irqs(void) void fixup_irqs(void)
{ {
unsigned int irq; unsigned int irq;
static int warned;
struct irq_desc *desc; struct irq_desc *desc;
for_each_irq_desc(irq, desc) { for_each_irq_desc(irq, desc) {
...@@ -236,8 +235,8 @@ void fixup_irqs(void) ...@@ -236,8 +235,8 @@ void fixup_irqs(void)
} }
if (desc->chip->set_affinity) if (desc->chip->set_affinity)
desc->chip->set_affinity(irq, affinity); desc->chip->set_affinity(irq, affinity);
else if (desc->action && !(warned++)) else if (desc->action)
printk("Cannot set affinity for irq %i\n", irq); printk_once("Cannot set affinity for irq %i\n", irq);
} }
#if 0 #if 0
......
...@@ -711,6 +711,21 @@ void __init setup_arch(char **cmdline_p) ...@@ -711,6 +711,21 @@ void __init setup_arch(char **cmdline_p)
printk(KERN_INFO "Command line: %s\n", boot_command_line); printk(KERN_INFO "Command line: %s\n", boot_command_line);
#endif #endif
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
#ifdef CONFIG_X86_64
/*
* Must call this twice: Once just to detect whether hardware doesn't
* support NX (so that the early EHCI debug console setup can safely
* call set_fixmap(), and then again after parsing early parameters to
* honor the respective command line option.
*/
check_efer();
#endif
parse_early_param();
/* VMI may relocate the fixmap; do this before touching ioremap area */ /* VMI may relocate the fixmap; do this before touching ioremap area */
vmi_init(); vmi_init();
...@@ -793,11 +808,6 @@ void __init setup_arch(char **cmdline_p) ...@@ -793,11 +808,6 @@ void __init setup_arch(char **cmdline_p)
#endif #endif
#endif #endif
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
parse_early_param();
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
check_efer(); check_efer();
#endif #endif
......
...@@ -1116,9 +1116,22 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus) ...@@ -1116,9 +1116,22 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
if (is_uv_system()) if (is_uv_system())
uv_system_init(); uv_system_init();
set_mtrr_aps_delayed_init();
out: out:
preempt_enable(); preempt_enable();
} }
void arch_enable_nonboot_cpus_begin(void)
{
set_mtrr_aps_delayed_init();
}
void arch_enable_nonboot_cpus_end(void)
{
mtrr_aps_init();
}
/* /*
* Early setup to make printk work. * Early setup to make printk work.
*/ */
...@@ -1140,6 +1153,7 @@ void __init native_smp_cpus_done(unsigned int max_cpus) ...@@ -1140,6 +1153,7 @@ void __init native_smp_cpus_done(unsigned int max_cpus)
setup_ioapic_dest(); setup_ioapic_dest();
#endif #endif
check_nmi_watchdog(); check_nmi_watchdog();
mtrr_aps_init();
} }
static int __initdata setup_possible_cpus = -1; static int __initdata setup_possible_cpus = -1;
......
...@@ -76,7 +76,7 @@ char ignore_fpu_irq; ...@@ -76,7 +76,7 @@ char ignore_fpu_irq;
* F0 0F bug workaround.. We have a special link segment * F0 0F bug workaround.. We have a special link segment
* for this. * for this.
*/ */
gate_desc idt_table[256] gate_desc idt_table[NR_VECTORS]
__attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, }; __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, };
#endif #endif
......
...@@ -2297,12 +2297,7 @@ static int emulator_cmpxchg_emulated(unsigned long addr, ...@@ -2297,12 +2297,7 @@ static int emulator_cmpxchg_emulated(unsigned long addr,
unsigned int bytes, unsigned int bytes,
struct kvm_vcpu *vcpu) struct kvm_vcpu *vcpu)
{ {
static int reported; printk_once(KERN_WARNING "kvm: emulating exchange as write\n");
if (!reported) {
reported = 1;
printk(KERN_WARNING "kvm: emulating exchange as write\n");
}
#ifndef CONFIG_X86_64 #ifndef CONFIG_X86_64
/* guests cmpxchg8b have to be emulated atomically */ /* guests cmpxchg8b have to be emulated atomically */
if (bytes == 8) { if (bytes == 8) {
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/highmem.h> #include <linux/highmem.h>
int is_io_mapping_possible(resource_size_t base, unsigned long size) static int is_io_mapping_possible(resource_size_t base, unsigned long size)
{ {
#if !defined(CONFIG_X86_PAE) && defined(CONFIG_PHYS_ADDR_T_64BIT) #if !defined(CONFIG_X86_PAE) && defined(CONFIG_PHYS_ADDR_T_64BIT)
/* There is no way to map greater than 1 << 32 address without PAE */ /* There is no way to map greater than 1 << 32 address without PAE */
...@@ -30,7 +30,30 @@ int is_io_mapping_possible(resource_size_t base, unsigned long size) ...@@ -30,7 +30,30 @@ int is_io_mapping_possible(resource_size_t base, unsigned long size)
#endif #endif
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(is_io_mapping_possible);
int iomap_create_wc(resource_size_t base, unsigned long size, pgprot_t *prot)
{
unsigned long flag = _PAGE_CACHE_WC;
int ret;
if (!is_io_mapping_possible(base, size))
return -EINVAL;
ret = io_reserve_memtype(base, base + size, &flag);
if (ret)
return ret;
*prot = __pgprot(__PAGE_KERNEL | flag);
return 0;
}
EXPORT_SYMBOL_GPL(iomap_create_wc);
void
iomap_free(resource_size_t base, unsigned long size)
{
io_free_memtype(base, base + size);
}
EXPORT_SYMBOL_GPL(iomap_free);
void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot) void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type, pgprot_t prot)
{ {
......
...@@ -228,24 +228,14 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, ...@@ -228,24 +228,14 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
retval = reserve_memtype(phys_addr, (u64)phys_addr + size, retval = reserve_memtype(phys_addr, (u64)phys_addr + size,
prot_val, &new_prot_val); prot_val, &new_prot_val);
if (retval) { if (retval) {
pr_debug("Warning: reserve_memtype returned %d\n", retval); printk(KERN_ERR "ioremap reserve_memtype failed %d\n", retval);
return NULL; return NULL;
} }
if (prot_val != new_prot_val) { if (prot_val != new_prot_val) {
/* if (!is_new_memtype_allowed(phys_addr, size,
* Do not fallback to certain memory types with certain prot_val, new_prot_val)) {
* requested type: printk(KERN_ERR
* - request is uc-, return cannot be write-back
* - request is uc-, return cannot be write-combine
* - request is write-combine, return cannot be write-back
*/
if ((prot_val == _PAGE_CACHE_UC_MINUS &&
(new_prot_val == _PAGE_CACHE_WB ||
new_prot_val == _PAGE_CACHE_WC)) ||
(prot_val == _PAGE_CACHE_WC &&
new_prot_val == _PAGE_CACHE_WB)) {
pr_debug(
"ioremap error for 0x%llx-0x%llx, requested 0x%lx, got 0x%lx\n", "ioremap error for 0x%llx-0x%llx, requested 0x%lx, got 0x%lx\n",
(unsigned long long)phys_addr, (unsigned long long)phys_addr,
(unsigned long long)(phys_addr + size), (unsigned long long)(phys_addr + size),
......
...@@ -822,6 +822,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, ...@@ -822,6 +822,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
{ {
struct cpa_data cpa; struct cpa_data cpa;
int ret, cache, checkalias; int ret, cache, checkalias;
unsigned long baddr = 0;
/* /*
* Check, if we are requested to change a not supported * Check, if we are requested to change a not supported
...@@ -853,6 +854,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, ...@@ -853,6 +854,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
*/ */
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
/*
* Save address for cache flush. *addr is modified in the call
* to __change_page_attr_set_clr() below.
*/
baddr = *addr;
} }
/* Must avoid aliasing mappings in the highmem code */ /* Must avoid aliasing mappings in the highmem code */
...@@ -900,7 +906,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, ...@@ -900,7 +906,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
cpa_flush_array(addr, numpages, cache, cpa_flush_array(addr, numpages, cache,
cpa.flags, pages); cpa.flags, pages);
} else } else
cpa_flush_range(*addr, numpages, cache); cpa_flush_range(baddr, numpages, cache);
} else } else
cpa_flush_all(cache); cpa_flush_all(cache);
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/rbtree.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -148,11 +149,10 @@ static char *cattr_name(unsigned long flags) ...@@ -148,11 +149,10 @@ static char *cattr_name(unsigned long flags)
* areas). All the aliases have the same cache attributes of course. * areas). All the aliases have the same cache attributes of course.
* Zero attributes are represented as holes. * Zero attributes are represented as holes.
* *
* Currently the data structure is a list because the number of mappings * The data structure is a list that is also organized as an rbtree
* are expected to be relatively small. If this should be a problem * sorted on the start address of memtype range.
* it could be changed to a rbtree or similar.
* *
* memtype_lock protects the whole list. * memtype_lock protects both the linear list and rbtree.
*/ */
struct memtype { struct memtype {
...@@ -160,11 +160,53 @@ struct memtype { ...@@ -160,11 +160,53 @@ struct memtype {
u64 end; u64 end;
unsigned long type; unsigned long type;
struct list_head nd; struct list_head nd;
struct rb_node rb;
}; };
static struct rb_root memtype_rbroot = RB_ROOT;
static LIST_HEAD(memtype_list); static LIST_HEAD(memtype_list);
static DEFINE_SPINLOCK(memtype_lock); /* protects memtype list */ static DEFINE_SPINLOCK(memtype_lock); /* protects memtype list */
static struct memtype *memtype_rb_search(struct rb_root *root, u64 start)
{
struct rb_node *node = root->rb_node;
struct memtype *last_lower = NULL;
while (node) {
struct memtype *data = container_of(node, struct memtype, rb);
if (data->start < start) {
last_lower = data;
node = node->rb_right;
} else if (data->start > start) {
node = node->rb_left;
} else
return data;
}
/* Will return NULL if there is no entry with its start <= start */
return last_lower;
}
static void memtype_rb_insert(struct rb_root *root, struct memtype *data)
{
struct rb_node **new = &(root->rb_node);
struct rb_node *parent = NULL;
while (*new) {
struct memtype *this = container_of(*new, struct memtype, rb);
parent = *new;
if (data->start <= this->start)
new = &((*new)->rb_left);
else if (data->start > this->start)
new = &((*new)->rb_right);
}
rb_link_node(&data->rb, parent, new);
rb_insert_color(&data->rb, root);
}
/* /*
* Does intersection of PAT memory type and MTRR memory type and returns * Does intersection of PAT memory type and MTRR memory type and returns
* the resulting memory type as PAT understands it. * the resulting memory type as PAT understands it.
...@@ -218,9 +260,6 @@ chk_conflict(struct memtype *new, struct memtype *entry, unsigned long *type) ...@@ -218,9 +260,6 @@ chk_conflict(struct memtype *new, struct memtype *entry, unsigned long *type)
return -EBUSY; return -EBUSY;
} }
static struct memtype *cached_entry;
static u64 cached_start;
static int pat_pagerange_is_ram(unsigned long start, unsigned long end) static int pat_pagerange_is_ram(unsigned long start, unsigned long end)
{ {
int ram_page = 0, not_rampage = 0; int ram_page = 0, not_rampage = 0;
...@@ -249,63 +288,61 @@ static int pat_pagerange_is_ram(unsigned long start, unsigned long end) ...@@ -249,63 +288,61 @@ static int pat_pagerange_is_ram(unsigned long start, unsigned long end)
} }
/* /*
* For RAM pages, mark the pages as non WB memory type using * For RAM pages, we use page flags to mark the pages with appropriate type.
* PageNonWB (PG_arch_1). We allow only one set_memory_uc() or * Here we do two pass:
* set_memory_wc() on a RAM page at a time before marking it as WB again. * - Find the memtype of all the pages in the range, look for any conflicts
* This is ok, because only one driver will be owning the page and * - In case of no conflicts, set the new memtype for pages in the range
* doing set_memory_*() calls.
* *
* For now, we use PageNonWB to track that the RAM page is being mapped * Caller must hold memtype_lock for atomicity.
* as non WB. In future, we will have to use one more flag
* (or some other mechanism in page_struct) to distinguish between
* UC and WC mapping.
*/ */
static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type, static int reserve_ram_pages_type(u64 start, u64 end, unsigned long req_type,
unsigned long *new_type) unsigned long *new_type)
{ {
struct page *page; struct page *page;
u64 pfn, end_pfn; u64 pfn;
if (req_type == _PAGE_CACHE_UC) {
/* We do not support strong UC */
WARN_ON_ONCE(1);
req_type = _PAGE_CACHE_UC_MINUS;
}
for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
unsigned long type;
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
if (page_mapped(page) || PageNonWB(page)) type = get_page_memtype(page);
goto out; if (type != -1) {
printk(KERN_INFO "reserve_ram_pages_type failed "
"0x%Lx-0x%Lx, track 0x%lx, req 0x%lx\n",
start, end, type, req_type);
if (new_type)
*new_type = type;
SetPageNonWB(page); return -EBUSY;
} }
return 0; }
if (new_type)
*new_type = req_type;
out: for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
end_pfn = pfn;
for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) {
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
ClearPageNonWB(page); set_page_memtype(page, req_type);
} }
return 0;
return -EINVAL;
} }
static int free_ram_pages_type(u64 start, u64 end) static int free_ram_pages_type(u64 start, u64 end)
{ {
struct page *page; struct page *page;
u64 pfn, end_pfn; u64 pfn;
for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) { for (pfn = (start >> PAGE_SHIFT); pfn < (end >> PAGE_SHIFT); ++pfn) {
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
if (page_mapped(page) || !PageNonWB(page)) set_page_memtype(page, -1);
goto out;
ClearPageNonWB(page);
} }
return 0; return 0;
out:
end_pfn = pfn;
for (pfn = (start >> PAGE_SHIFT); pfn < end_pfn; ++pfn) {
page = pfn_to_page(pfn);
SetPageNonWB(page);
}
return -EINVAL;
} }
/* /*
...@@ -339,6 +376,8 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -339,6 +376,8 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
if (new_type) { if (new_type) {
if (req_type == -1) if (req_type == -1)
*new_type = _PAGE_CACHE_WB; *new_type = _PAGE_CACHE_WB;
else if (req_type == _PAGE_CACHE_WC)
*new_type = _PAGE_CACHE_UC_MINUS;
else else
*new_type = req_type & _PAGE_CACHE_MASK; *new_type = req_type & _PAGE_CACHE_MASK;
} }
...@@ -364,11 +403,16 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -364,11 +403,16 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
*new_type = actual_type; *new_type = actual_type;
is_range_ram = pat_pagerange_is_ram(start, end); is_range_ram = pat_pagerange_is_ram(start, end);
if (is_range_ram == 1) if (is_range_ram == 1) {
return reserve_ram_pages_type(start, end, req_type,
new_type); spin_lock(&memtype_lock);
else if (is_range_ram < 0) err = reserve_ram_pages_type(start, end, req_type, new_type);
spin_unlock(&memtype_lock);
return err;
} else if (is_range_ram < 0) {
return -EINVAL; return -EINVAL;
}
new = kmalloc(sizeof(struct memtype), GFP_KERNEL); new = kmalloc(sizeof(struct memtype), GFP_KERNEL);
if (!new) if (!new)
...@@ -380,17 +424,11 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -380,17 +424,11 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
spin_lock(&memtype_lock); spin_lock(&memtype_lock);
if (cached_entry && start >= cached_start)
entry = cached_entry;
else
entry = list_entry(&memtype_list, struct memtype, nd);
/* Search for existing mapping that overlaps the current range */ /* Search for existing mapping that overlaps the current range */
where = NULL; where = NULL;
list_for_each_entry_continue(entry, &memtype_list, nd) { list_for_each_entry(entry, &memtype_list, nd) {
if (end <= entry->start) { if (end <= entry->start) {
where = entry->nd.prev; where = entry->nd.prev;
cached_entry = list_entry(where, struct memtype, nd);
break; break;
} else if (start <= entry->start) { /* end > entry->start */ } else if (start <= entry->start) { /* end > entry->start */
err = chk_conflict(new, entry, new_type); err = chk_conflict(new, entry, new_type);
...@@ -398,8 +436,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -398,8 +436,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
dprintk("Overlap at 0x%Lx-0x%Lx\n", dprintk("Overlap at 0x%Lx-0x%Lx\n",
entry->start, entry->end); entry->start, entry->end);
where = entry->nd.prev; where = entry->nd.prev;
cached_entry = list_entry(where,
struct memtype, nd);
} }
break; break;
} else if (start < entry->end) { /* start > entry->start */ } else if (start < entry->end) { /* start > entry->start */
...@@ -407,8 +443,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -407,8 +443,6 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
if (!err) { if (!err) {
dprintk("Overlap at 0x%Lx-0x%Lx\n", dprintk("Overlap at 0x%Lx-0x%Lx\n",
entry->start, entry->end); entry->start, entry->end);
cached_entry = list_entry(entry->nd.prev,
struct memtype, nd);
/* /*
* Move to right position in the linked * Move to right position in the linked
...@@ -436,13 +470,13 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -436,13 +470,13 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
return err; return err;
} }
cached_start = start;
if (where) if (where)
list_add(&new->nd, where); list_add(&new->nd, where);
else else
list_add_tail(&new->nd, &memtype_list); list_add_tail(&new->nd, &memtype_list);
memtype_rb_insert(&memtype_rbroot, new);
spin_unlock(&memtype_lock); spin_unlock(&memtype_lock);
dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n", dprintk("reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n",
...@@ -454,7 +488,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type, ...@@ -454,7 +488,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
int free_memtype(u64 start, u64 end) int free_memtype(u64 start, u64 end)
{ {
struct memtype *entry; struct memtype *entry, *saved_entry;
int err = -EINVAL; int err = -EINVAL;
int is_range_ram; int is_range_ram;
...@@ -466,23 +500,58 @@ int free_memtype(u64 start, u64 end) ...@@ -466,23 +500,58 @@ int free_memtype(u64 start, u64 end)
return 0; return 0;
is_range_ram = pat_pagerange_is_ram(start, end); is_range_ram = pat_pagerange_is_ram(start, end);
if (is_range_ram == 1) if (is_range_ram == 1) {
return free_ram_pages_type(start, end);
else if (is_range_ram < 0) spin_lock(&memtype_lock);
err = free_ram_pages_type(start, end);
spin_unlock(&memtype_lock);
return err;
} else if (is_range_ram < 0) {
return -EINVAL; return -EINVAL;
}
spin_lock(&memtype_lock); spin_lock(&memtype_lock);
list_for_each_entry(entry, &memtype_list, nd) {
entry = memtype_rb_search(&memtype_rbroot, start);
if (unlikely(entry == NULL))
goto unlock_ret;
/*
* Saved entry points to an entry with start same or less than what
* we searched for. Now go through the list in both directions to look
* for the entry that matches with both start and end, with list stored
* in sorted start address
*/
saved_entry = entry;
list_for_each_entry_from(entry, &memtype_list, nd) {
if (entry->start == start && entry->end == end) { if (entry->start == start && entry->end == end) {
if (cached_entry == entry || cached_start == start) rb_erase(&entry->rb, &memtype_rbroot);
cached_entry = NULL; list_del(&entry->nd);
kfree(entry);
err = 0;
break;
} else if (entry->start > start) {
break;
}
}
if (!err)
goto unlock_ret;
entry = saved_entry;
list_for_each_entry_reverse(entry, &memtype_list, nd) {
if (entry->start == start && entry->end == end) {
rb_erase(&entry->rb, &memtype_rbroot);
list_del(&entry->nd); list_del(&entry->nd);
kfree(entry); kfree(entry);
err = 0; err = 0;
break; break;
} else if (entry->start < start) {
break;
} }
} }
unlock_ret:
spin_unlock(&memtype_lock); spin_unlock(&memtype_lock);
if (err) { if (err) {
...@@ -496,6 +565,101 @@ int free_memtype(u64 start, u64 end) ...@@ -496,6 +565,101 @@ int free_memtype(u64 start, u64 end)
} }
/**
* lookup_memtype - Looksup the memory type for a physical address
* @paddr: physical address of which memory type needs to be looked up
*
* Only to be called when PAT is enabled
*
* Returns _PAGE_CACHE_WB, _PAGE_CACHE_WC, _PAGE_CACHE_UC_MINUS or
* _PAGE_CACHE_UC
*/
static unsigned long lookup_memtype(u64 paddr)
{
int rettype = _PAGE_CACHE_WB;
struct memtype *entry;
if (is_ISA_range(paddr, paddr + PAGE_SIZE - 1))
return rettype;
if (pat_pagerange_is_ram(paddr, paddr + PAGE_SIZE)) {
struct page *page;
spin_lock(&memtype_lock);
page = pfn_to_page(paddr >> PAGE_SHIFT);
rettype = get_page_memtype(page);
spin_unlock(&memtype_lock);
/*
* -1 from get_page_memtype() implies RAM page is in its
* default state and not reserved, and hence of type WB
*/
if (rettype == -1)
rettype = _PAGE_CACHE_WB;
return rettype;
}
spin_lock(&memtype_lock);
entry = memtype_rb_search(&memtype_rbroot, paddr);
if (entry != NULL)
rettype = entry->type;
else
rettype = _PAGE_CACHE_UC_MINUS;
spin_unlock(&memtype_lock);
return rettype;
}
/**
* io_reserve_memtype - Request a memory type mapping for a region of memory
* @start: start (physical address) of the region
* @end: end (physical address) of the region
* @type: A pointer to memtype, with requested type. On success, requested
* or any other compatible type that was available for the region is returned
*
* On success, returns 0
* On failure, returns non-zero
*/
int io_reserve_memtype(resource_size_t start, resource_size_t end,
unsigned long *type)
{
resource_size_t size = end - start;
unsigned long req_type = *type;
unsigned long new_type;
int ret;
WARN_ON_ONCE(iomem_map_sanity_check(start, size));
ret = reserve_memtype(start, end, req_type, &new_type);
if (ret)
goto out_err;
if (!is_new_memtype_allowed(start, size, req_type, new_type))
goto out_free;
if (kernel_map_sync_memtype(start, size, new_type) < 0)
goto out_free;
*type = new_type;
return 0;
out_free:
free_memtype(start, end);
ret = -EBUSY;
out_err:
return ret;
}
/**
* io_free_memtype - Release a memory type mapping for a region of memory
* @start: start (physical address) of the region
* @end: end (physical address) of the region
*/
void io_free_memtype(resource_size_t start, resource_size_t end)
{
free_memtype(start, end);
}
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
unsigned long size, pgprot_t vma_prot) unsigned long size, pgprot_t vma_prot)
{ {
...@@ -577,7 +741,7 @@ int kernel_map_sync_memtype(u64 base, unsigned long size, unsigned long flags) ...@@ -577,7 +741,7 @@ int kernel_map_sync_memtype(u64 base, unsigned long size, unsigned long flags)
{ {
unsigned long id_sz; unsigned long id_sz;
if (!pat_enabled || base >= __pa(high_memory)) if (base >= __pa(high_memory))
return 0; return 0;
id_sz = (__pa(high_memory) < base + size) ? id_sz = (__pa(high_memory) < base + size) ?
...@@ -612,11 +776,29 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot, ...@@ -612,11 +776,29 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
is_ram = pat_pagerange_is_ram(paddr, paddr + size); is_ram = pat_pagerange_is_ram(paddr, paddr + size);
/* /*
* reserve_pfn_range() doesn't support RAM pages. Maintain the current * reserve_pfn_range() for RAM pages. We do not refcount to keep
* behavior with RAM pages by returning success. * track of number of mappings of RAM pages. We can assert that
* the type requested matches the type of first page in the range.
*/ */
if (is_ram != 0) if (is_ram) {
if (!pat_enabled)
return 0;
flags = lookup_memtype(paddr);
if (want_flags != flags) {
printk(KERN_WARNING
"%s:%d map pfn RAM range req %s for %Lx-%Lx, got %s\n",
current->comm, current->pid,
cattr_name(want_flags),
(unsigned long long)paddr,
(unsigned long long)(paddr + size),
cattr_name(flags));
*vma_prot = __pgprot((pgprot_val(*vma_prot) &
(~_PAGE_CACHE_MASK)) |
flags);
}
return 0; return 0;
}
ret = reserve_memtype(paddr, paddr + size, want_flags, &flags); ret = reserve_memtype(paddr, paddr + size, want_flags, &flags);
if (ret) if (ret)
...@@ -678,14 +860,6 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) ...@@ -678,14 +860,6 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
unsigned long vma_size = vma->vm_end - vma->vm_start; unsigned long vma_size = vma->vm_end - vma->vm_start;
pgprot_t pgprot; pgprot_t pgprot;
if (!pat_enabled)
return 0;
/*
* For now, only handle remap_pfn_range() vmas where
* is_linear_pfn_mapping() == TRUE. Handling of
* vm_insert_pfn() is TBD.
*/
if (is_linear_pfn_mapping(vma)) { if (is_linear_pfn_mapping(vma)) {
/* /*
* reserve the whole chunk covered by vma. We need the * reserve the whole chunk covered by vma. We need the
...@@ -713,23 +887,24 @@ int track_pfn_vma_copy(struct vm_area_struct *vma) ...@@ -713,23 +887,24 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot, int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
unsigned long pfn, unsigned long size) unsigned long pfn, unsigned long size)
{ {
unsigned long flags;
resource_size_t paddr; resource_size_t paddr;
unsigned long vma_size = vma->vm_end - vma->vm_start; unsigned long vma_size = vma->vm_end - vma->vm_start;
if (!pat_enabled)
return 0;
/*
* For now, only handle remap_pfn_range() vmas where
* is_linear_pfn_mapping() == TRUE. Handling of
* vm_insert_pfn() is TBD.
*/
if (is_linear_pfn_mapping(vma)) { if (is_linear_pfn_mapping(vma)) {
/* reserve the whole chunk starting from vm_pgoff */ /* reserve the whole chunk starting from vm_pgoff */
paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
return reserve_pfn_range(paddr, vma_size, prot, 0); return reserve_pfn_range(paddr, vma_size, prot, 0);
} }
if (!pat_enabled)
return 0;
/* for vm_insert_pfn and friends, we set prot based on lookup */
flags = lookup_memtype(pfn << PAGE_SHIFT);
*prot = __pgprot((pgprot_val(vma->vm_page_prot) & (~_PAGE_CACHE_MASK)) |
flags);
return 0; return 0;
} }
...@@ -744,14 +919,6 @@ void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn, ...@@ -744,14 +919,6 @@ void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
resource_size_t paddr; resource_size_t paddr;
unsigned long vma_size = vma->vm_end - vma->vm_start; unsigned long vma_size = vma->vm_end - vma->vm_start;
if (!pat_enabled)
return;
/*
* For now, only handle remap_pfn_range() vmas where
* is_linear_pfn_mapping() == TRUE. Handling of
* vm_insert_pfn() is TBD.
*/
if (is_linear_pfn_mapping(vma)) { if (is_linear_pfn_mapping(vma)) {
/* free the whole chunk starting from vm_pgoff */ /* free the whole chunk starting from vm_pgoff */
paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT; paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
......
...@@ -242,7 +242,7 @@ static void __restore_processor_state(struct saved_context *ctxt) ...@@ -242,7 +242,7 @@ static void __restore_processor_state(struct saved_context *ctxt)
fix_processor_context(); fix_processor_context();
do_fpu_end(); do_fpu_end();
mtrr_ap_init(); mtrr_bp_restore();
#ifdef CONFIG_X86_OLD_MCE #ifdef CONFIG_X86_OLD_MCE
mcheck_init(&boot_cpu_data); mcheck_init(&boot_cpu_data);
......
...@@ -49,23 +49,30 @@ static inline struct io_mapping * ...@@ -49,23 +49,30 @@ static inline struct io_mapping *
io_mapping_create_wc(resource_size_t base, unsigned long size) io_mapping_create_wc(resource_size_t base, unsigned long size)
{ {
struct io_mapping *iomap; struct io_mapping *iomap;
pgprot_t prot;
if (!is_io_mapping_possible(base, size))
return NULL;
iomap = kmalloc(sizeof(*iomap), GFP_KERNEL); iomap = kmalloc(sizeof(*iomap), GFP_KERNEL);
if (!iomap) if (!iomap)
return NULL; goto out_err;
if (iomap_create_wc(base, size, &prot))
goto out_free;
iomap->base = base; iomap->base = base;
iomap->size = size; iomap->size = size;
iomap->prot = pgprot_writecombine(__pgprot(__PAGE_KERNEL)); iomap->prot = prot;
return iomap; return iomap;
out_free:
kfree(iomap);
out_err:
return NULL;
} }
static inline void static inline void
io_mapping_free(struct io_mapping *mapping) io_mapping_free(struct io_mapping *mapping)
{ {
iomap_free(mapping->base, mapping->size);
kfree(mapping); kfree(mapping);
} }
......
...@@ -99,7 +99,7 @@ enum pageflags { ...@@ -99,7 +99,7 @@ enum pageflags {
#ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT #ifdef CONFIG_HAVE_MLOCKED_PAGE_BIT
PG_mlocked, /* Page is vma mlocked */ PG_mlocked, /* Page is vma mlocked */
#endif #endif
#ifdef CONFIG_IA64_UNCACHED_ALLOCATOR #ifdef CONFIG_ARCH_USES_PG_UNCACHED
PG_uncached, /* Page has been mapped as uncached */ PG_uncached, /* Page has been mapped as uncached */
#endif #endif
__NR_PAGEFLAGS, __NR_PAGEFLAGS,
...@@ -257,7 +257,7 @@ PAGEFLAG_FALSE(Mlocked) ...@@ -257,7 +257,7 @@ PAGEFLAG_FALSE(Mlocked)
SETPAGEFLAG_NOOP(Mlocked) TESTCLEARFLAG_FALSE(Mlocked) SETPAGEFLAG_NOOP(Mlocked) TESTCLEARFLAG_FALSE(Mlocked)
#endif #endif
#ifdef CONFIG_IA64_UNCACHED_ALLOCATOR #ifdef CONFIG_ARCH_USES_PG_UNCACHED
PAGEFLAG(Uncached, uncached) PAGEFLAG(Uncached, uncached)
#else #else
PAGEFLAG_FALSE(Uncached) PAGEFLAG_FALSE(Uncached)
......
...@@ -413,6 +413,14 @@ int disable_nonboot_cpus(void) ...@@ -413,6 +413,14 @@ int disable_nonboot_cpus(void)
return error; return error;
} }
void __weak arch_enable_nonboot_cpus_begin(void)
{
}
void __weak arch_enable_nonboot_cpus_end(void)
{
}
void __ref enable_nonboot_cpus(void) void __ref enable_nonboot_cpus(void)
{ {
int cpu, error; int cpu, error;
...@@ -424,6 +432,9 @@ void __ref enable_nonboot_cpus(void) ...@@ -424,6 +432,9 @@ void __ref enable_nonboot_cpus(void)
goto out; goto out;
printk("Enabling non-boot CPUs ...\n"); printk("Enabling non-boot CPUs ...\n");
arch_enable_nonboot_cpus_begin();
for_each_cpu(cpu, frozen_cpus) { for_each_cpu(cpu, frozen_cpus) {
error = _cpu_up(cpu, 1); error = _cpu_up(cpu, 1);
if (!error) { if (!error) {
...@@ -432,6 +443,9 @@ void __ref enable_nonboot_cpus(void) ...@@ -432,6 +443,9 @@ void __ref enable_nonboot_cpus(void)
} }
printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error); printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error);
} }
arch_enable_nonboot_cpus_end();
cpumask_clear(frozen_cpus); cpumask_clear(frozen_cpus);
out: out:
cpu_maps_update_done(); cpu_maps_update_done();
......
...@@ -176,6 +176,11 @@ void generic_smp_call_function_interrupt(void) ...@@ -176,6 +176,11 @@ void generic_smp_call_function_interrupt(void)
struct call_function_data *data; struct call_function_data *data;
int cpu = get_cpu(); int cpu = get_cpu();
/*
* Shouldn't receive this interrupt on a cpu that is not yet online.
*/
WARN_ON_ONCE(!cpu_online(cpu));
/* /*
* Ensure entry is visible on call_function_queue after we have * Ensure entry is visible on call_function_queue after we have
* entered the IPI. See comment in smp_call_function_many. * entered the IPI. See comment in smp_call_function_many.
...@@ -230,6 +235,11 @@ void generic_smp_call_function_single_interrupt(void) ...@@ -230,6 +235,11 @@ void generic_smp_call_function_single_interrupt(void)
unsigned int data_flags; unsigned int data_flags;
LIST_HEAD(list); LIST_HEAD(list);
/*
* Shouldn't receive this interrupt on a cpu that is not yet online.
*/
WARN_ON_ONCE(!cpu_online(smp_processor_id()));
spin_lock(&q->lock); spin_lock(&q->lock);
list_replace_init(&q->list, &list); list_replace_init(&q->list, &list);
spin_unlock(&q->lock); spin_unlock(&q->lock);
...@@ -285,8 +295,14 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info, ...@@ -285,8 +295,14 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
*/ */
this_cpu = get_cpu(); this_cpu = get_cpu();
/* Can deadlock when called with interrupts disabled */ /*
WARN_ON_ONCE(irqs_disabled() && !oops_in_progress); * Can deadlock when called with interrupts disabled.
* We allow cpu's that are not yet online though, as no one else can
* send smp call function interrupt to this cpu and as such deadlocks
* can't happen.
*/
WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
&& !oops_in_progress);
if (cpu == this_cpu) { if (cpu == this_cpu) {
local_irq_save(flags); local_irq_save(flags);
...@@ -329,8 +345,14 @@ void __smp_call_function_single(int cpu, struct call_single_data *data, ...@@ -329,8 +345,14 @@ void __smp_call_function_single(int cpu, struct call_single_data *data,
{ {
csd_lock(data); csd_lock(data);
/* Can deadlock when called with interrupts disabled */ /*
WARN_ON_ONCE(wait && irqs_disabled() && !oops_in_progress); * Can deadlock when called with interrupts disabled.
* We allow cpu's that are not yet online though, as no one else can
* send smp call function interrupt to this cpu and as such deadlocks
* can't happen.
*/
WARN_ON_ONCE(cpu_online(smp_processor_id()) && wait && irqs_disabled()
&& !oops_in_progress);
generic_exec_single(cpu, data, wait); generic_exec_single(cpu, data, wait);
} }
...@@ -365,8 +387,14 @@ void smp_call_function_many(const struct cpumask *mask, ...@@ -365,8 +387,14 @@ void smp_call_function_many(const struct cpumask *mask,
unsigned long flags; unsigned long flags;
int cpu, next_cpu, this_cpu = smp_processor_id(); int cpu, next_cpu, this_cpu = smp_processor_id();
/* Can deadlock when called with interrupts disabled */ /*
WARN_ON_ONCE(irqs_disabled() && !oops_in_progress); * Can deadlock when called with interrupts disabled.
* We allow cpu's that are not yet online though, as no one else can
* send smp call function interrupt to this cpu and as such deadlocks
* can't happen.
*/
WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled()
&& !oops_in_progress);
/* So, what's a CPU they want? Ignoring this one. */ /* So, what's a CPU they want? Ignoring this one. */
cpu = cpumask_first_and(mask, cpu_online_mask); cpu = cpumask_first_and(mask, cpu_online_mask);
......
...@@ -153,7 +153,7 @@ config MEMORY_HOTREMOVE ...@@ -153,7 +153,7 @@ config MEMORY_HOTREMOVE
# #
config PAGEFLAGS_EXTENDED config PAGEFLAGS_EXTENDED
def_bool y def_bool y
depends on 64BIT || SPARSEMEM_VMEMMAP || !NUMA || !SPARSEMEM depends on 64BIT || SPARSEMEM_VMEMMAP || !SPARSEMEM
# Heavily threaded applications may benefit from splitting the mm-wide # Heavily threaded applications may benefit from splitting the mm-wide
# page_table_lock, so that faults on different parts of the user address # page_table_lock, so that faults on different parts of the user address
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment