1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303 |
- /* Generic MTRR (Memory Type Range Register) driver.
- Copyright (C) 1997-2000 Richard Gooch
- Copyright (c) 2002 Patrick Mochel
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- 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
- 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
- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- Richard Gooch may be reached by email at rgooch@atnf.csiro.au
- The postal address is:
- Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
- Source: "Pentium Pro Family Developer's Manual, Volume 3:
- Operating System Writer's Guide" (Intel document number 242692),
- section 11.11.7
- This was cleaned and made readable by Patrick Mochel <mochel@osdl.org>
- on 6-7 March 2002.
- Source: Intel Architecture Software Developers Manual, Volume 3:
- System Programming Guide; Section 9.11. (1997 edition - PPro).
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/pci.h>
- #include <linux/smp.h>
- #include <linux/cpu.h>
- #include <linux/mutex.h>
- #include <linux/sort.h>
- #include <asm/e820.h>
- #include <asm/mtrr.h>
- #include <asm/uaccess.h>
- #include <asm/processor.h>
- #include <asm/msr.h>
- #include <asm/kvm_para.h>
- #include "mtrr.h"
- u32 num_var_ranges = 0;
- unsigned int mtrr_usage_table[MAX_VAR_RANGES];
- static DEFINE_MUTEX(mtrr_mutex);
- u64 size_or_mask, size_and_mask;
- static struct mtrr_ops * mtrr_ops[X86_VENDOR_NUM] = {};
- struct mtrr_ops * mtrr_if = NULL;
- static void set_mtrr(unsigned int reg, unsigned long base,
- unsigned long size, mtrr_type type);
- void set_mtrr_ops(struct mtrr_ops * ops)
- {
- if (ops->vendor && ops->vendor < X86_VENDOR_NUM)
- mtrr_ops[ops->vendor] = ops;
- }
- /* Returns non-zero if we have the write-combining memory type */
- static int have_wrcomb(void)
- {
- struct pci_dev *dev;
- u8 rev;
-
- if ((dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) {
- /* 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 &&
- dev->device == PCI_DEVICE_ID_SERVERWORKS_LE) {
- pci_read_config_byte(dev, PCI_CLASS_REVISION, &rev);
- if (rev <= 5) {
- printk(KERN_INFO "mtrr: Serverworks LE rev < 6 detected. Write-combining disabled.\n");
- pci_dev_put(dev);
- return 0;
- }
- }
- /* Intel 450NX errata # 23. Non ascending cacheline evictions to
- write combining memory may resulting in data corruption */
- if (dev->vendor == PCI_VENDOR_ID_INTEL &&
- dev->device == PCI_DEVICE_ID_INTEL_82451NX) {
- printk(KERN_INFO "mtrr: Intel 450NX MMC detected. Write-combining disabled.\n");
- pci_dev_put(dev);
- return 0;
- }
- pci_dev_put(dev);
- }
- return (mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0);
- }
- /* This function returns the number of variable MTRRs */
- static void __init set_num_var_ranges(void)
- {
- unsigned long config = 0, dummy;
- if (use_intel()) {
- rdmsr(MTRRcap_MSR, config, dummy);
- } else if (is_cpu(AMD))
- config = 2;
- else if (is_cpu(CYRIX) || is_cpu(CENTAUR))
- config = 8;
- num_var_ranges = config & 0xff;
- }
- static void __init init_table(void)
- {
- int i, max;
- max = num_var_ranges;
- for (i = 0; i < max; i++)
- mtrr_usage_table[i] = 1;
- }
- struct set_mtrr_data {
- atomic_t count;
- atomic_t gate;
- unsigned long smp_base;
- unsigned long smp_size;
- unsigned int smp_reg;
- mtrr_type smp_type;
- };
- static void ipi_handler(void *info)
- /* [SUMMARY] Synchronisation handler. Executed by "other" CPUs.
- [RETURNS] Nothing.
- */
- {
- #ifdef CONFIG_SMP
- struct set_mtrr_data *data = info;
- unsigned long flags;
- local_irq_save(flags);
- atomic_dec(&data->count);
- while(!atomic_read(&data->gate))
- cpu_relax();
- /* The master has cleared me to execute */
- if (data->smp_reg != ~0U)
- mtrr_if->set(data->smp_reg, data->smp_base,
- data->smp_size, data->smp_type);
- else
- mtrr_if->set_all();
- atomic_dec(&data->count);
- while(atomic_read(&data->gate))
- cpu_relax();
- atomic_dec(&data->count);
- local_irq_restore(flags);
- #endif
- }
- static inline int types_compatible(mtrr_type type1, mtrr_type type2) {
- return type1 == MTRR_TYPE_UNCACHABLE ||
- type2 == MTRR_TYPE_UNCACHABLE ||
- (type1 == MTRR_TYPE_WRTHROUGH && type2 == MTRR_TYPE_WRBACK) ||
- (type1 == MTRR_TYPE_WRBACK && type2 == MTRR_TYPE_WRTHROUGH);
- }
- /**
- * set_mtrr - update mtrrs on all processors
- * @reg: mtrr in question
- * @base: mtrr base
- * @size: mtrr size
- * @type: mtrr type
- *
- * This is kinda tricky, but fortunately, Intel spelled it out for us cleanly:
- *
- * 1. Send IPI to do the following:
- * 2. Disable Interrupts
- * 3. Wait for all procs to do so
- * 4. Enter no-fill cache mode
- * 5. Flush caches
- * 6. Clear PGE bit
- * 7. Flush all TLBs
- * 8. Disable all range registers
- * 9. Update the MTRRs
- * 10. Enable all range registers
- * 11. Flush all TLBs and caches again
- * 12. Enter normal cache mode and reenable caching
- * 13. Set PGE
- * 14. Wait for buddies to catch up
- * 15. Enable interrupts.
- *
- * What does that mean for us? Well, first we set data.count to the number
- * 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.
- * 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
- * differently, so we call mtrr_if->set() callback and let them take care of it.
- * When they're done, they again decrement data->count and wait for data.gate 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.
- *
- * Note that the mechanism is the same for UP systems, too; all the SMP stuff
- * becomes nops.
- */
- static void set_mtrr(unsigned int reg, unsigned long base,
- unsigned long size, mtrr_type type)
- {
- struct set_mtrr_data data;
- unsigned long flags;
- data.smp_reg = reg;
- data.smp_base = base;
- data.smp_size = size;
- data.smp_type = type;
- atomic_set(&data.count, num_booting_cpus() - 1);
- /* make sure data.count is visible before unleashing other CPUs */
- smp_wmb();
- atomic_set(&data.gate,0);
- /* Start the ball rolling on other CPUs */
- if (smp_call_function(ipi_handler, &data, 1, 0) != 0)
- panic("mtrr: timed out waiting for other CPUs\n");
- local_irq_save(flags);
- while(atomic_read(&data.count))
- cpu_relax();
- /* ok, reset count and toggle gate */
- atomic_set(&data.count, num_booting_cpus() - 1);
- smp_wmb();
- atomic_set(&data.gate,1);
- /* do our MTRR business */
- /* HACK!
- * 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
- * to replicate across all the APs.
- * If we're doing that @reg is set to something special...
- */
- if (reg != ~0U)
- mtrr_if->set(reg,base,size,type);
- /* wait for the others */
- while(atomic_read(&data.count))
- cpu_relax();
- atomic_set(&data.count, num_booting_cpus() - 1);
- smp_wmb();
- atomic_set(&data.gate,0);
- /*
- * Wait here for everyone to have seen the gate change
- * So we're the last ones to touch 'data'
- */
- while(atomic_read(&data.count))
- cpu_relax();
- local_irq_restore(flags);
- }
- /**
- * mtrr_add_page - Add a memory type region
- * @base: Physical base address of region in pages (in units of 4 kB!)
- * @size: Physical size of region in pages (4 kB)
- * @type: Type of MTRR desired
- * @increment: If this is true do usage counting on the region
- *
- * Memory type region registers control the caching on newer Intel and
- * non Intel processors. This function allows drivers to request an
- * MTRR is added. The details and hardware specifics of each processor's
- * implementation are hidden from the caller, but nevertheless the
- * caller should expect to need to provide a power of two size on an
- * equivalent power of two boundary.
- *
- * If the region cannot be added either because all regions are in use
- * or the CPU cannot support it a negative value is returned. On success
- * the register number for this entry is returned, but should be treated
- * as a cookie only.
- *
- * On a multiprocessor machine the changes are made to all processors.
- * This is required on x86 by the Intel processors.
- *
- * The available types are
- *
- * %MTRR_TYPE_UNCACHABLE - No caching
- *
- * %MTRR_TYPE_WRBACK - Write data back in bursts whenever
- *
- * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts
- *
- * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes
- *
- * BUGS: Needs a quiet flag for the cases where drivers do not mind
- * failures and do not wish system log messages to be sent.
- */
- int mtrr_add_page(unsigned long base, unsigned long size,
- unsigned int type, bool increment)
- {
- int i, replace, error;
- mtrr_type ltype;
- unsigned long lbase, lsize;
- if (!mtrr_if)
- return -ENXIO;
-
- if ((error = mtrr_if->validate_add_page(base,size,type)))
- return error;
- if (type >= MTRR_NUM_TYPES) {
- printk(KERN_WARNING "mtrr: type: %u invalid\n", type);
- return -EINVAL;
- }
- /* If the type is WC, check that this processor supports it */
- if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb()) {
- printk(KERN_WARNING
- "mtrr: your processor doesn't support write-combining\n");
- return -ENOSYS;
- }
- if (!size) {
- printk(KERN_WARNING "mtrr: zero sized request\n");
- return -EINVAL;
- }
- if (base & size_or_mask || size & size_or_mask) {
- printk(KERN_WARNING "mtrr: base or size exceeds the MTRR width\n");
- return -EINVAL;
- }
- error = -EINVAL;
- replace = -1;
- /* No CPU hotplug when we change MTRR entries */
- get_online_cpus();
- /* Search for existing MTRR */
- mutex_lock(&mtrr_mutex);
- for (i = 0; i < num_var_ranges; ++i) {
- mtrr_if->get(i, &lbase, &lsize, <ype);
- if (!lsize || base > lbase + lsize - 1 || base + size - 1 < lbase)
- continue;
- /* 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) {
- /* New region encloses an existing region */
- if (type == ltype) {
- replace = replace == -1 ? i : -2;
- continue;
- }
- else if (types_compatible(type, ltype))
- continue;
- }
- printk(KERN_WARNING
- "mtrr: 0x%lx000,0x%lx000 overlaps existing"
- " 0x%lx000,0x%lx000\n", base, size, lbase,
- lsize);
- goto out;
- }
- /* New region is enclosed by an existing region */
- if (ltype != type) {
- if (types_compatible(type, ltype))
- continue;
- printk (KERN_WARNING "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n",
- base, size, mtrr_attrib_to_str(ltype),
- mtrr_attrib_to_str(type));
- goto out;
- }
- if (increment)
- ++mtrr_usage_table[i];
- error = i;
- goto out;
- }
- /* Search for an empty MTRR */
- i = mtrr_if->get_free_region(base, size, replace);
- if (i >= 0) {
- set_mtrr(i, base, size, type);
- if (likely(replace < 0)) {
- mtrr_usage_table[i] = 1;
- } else {
- mtrr_usage_table[i] = mtrr_usage_table[replace];
- if (increment)
- mtrr_usage_table[i]++;
- if (unlikely(replace != i)) {
- set_mtrr(replace, 0, 0, 0);
- mtrr_usage_table[replace] = 0;
- }
- }
- } else
- printk(KERN_INFO "mtrr: no more MTRRs available\n");
- error = i;
- out:
- mutex_unlock(&mtrr_mutex);
- put_online_cpus();
- return error;
- }
- static int mtrr_check(unsigned long base, unsigned long size)
- {
- if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) {
- printk(KERN_WARNING
- "mtrr: size and base must be multiples of 4 kiB\n");
- printk(KERN_DEBUG
- "mtrr: size: 0x%lx base: 0x%lx\n", size, base);
- dump_stack();
- return -1;
- }
- return 0;
- }
- /**
- * mtrr_add - Add a memory type region
- * @base: Physical base address of region
- * @size: Physical size of region
- * @type: Type of MTRR desired
- * @increment: If this is true do usage counting on the region
- *
- * Memory type region registers control the caching on newer Intel and
- * non Intel processors. This function allows drivers to request an
- * MTRR is added. The details and hardware specifics of each processor's
- * implementation are hidden from the caller, but nevertheless the
- * caller should expect to need to provide a power of two size on an
- * equivalent power of two boundary.
- *
- * If the region cannot be added either because all regions are in use
- * or the CPU cannot support it a negative value is returned. On success
- * the register number for this entry is returned, but should be treated
- * as a cookie only.
- *
- * On a multiprocessor machine the changes are made to all processors.
- * This is required on x86 by the Intel processors.
- *
- * The available types are
- *
- * %MTRR_TYPE_UNCACHABLE - No caching
- *
- * %MTRR_TYPE_WRBACK - Write data back in bursts whenever
- *
- * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts
- *
- * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes
- *
- * BUGS: Needs a quiet flag for the cases where drivers do not mind
- * failures and do not wish system log messages to be sent.
- */
- int
- mtrr_add(unsigned long base, unsigned long size, unsigned int type,
- bool increment)
- {
- if (mtrr_check(base, size))
- return -EINVAL;
- return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type,
- increment);
- }
- /**
- * mtrr_del_page - delete a memory type region
- * @reg: Register returned by mtrr_add
- * @base: Physical base address
- * @size: Size of region
- *
- * If register is supplied then base and size are ignored. This is
- * how drivers should call it.
- *
- * Releases an MTRR region. If the usage count drops to zero the
- * register is freed and the region returns to default state.
- * On success the register is returned, on failure a negative error
- * code.
- */
- int mtrr_del_page(int reg, unsigned long base, unsigned long size)
- {
- int i, max;
- mtrr_type ltype;
- unsigned long lbase, lsize;
- int error = -EINVAL;
- if (!mtrr_if)
- return -ENXIO;
- max = num_var_ranges;
- /* No CPU hotplug when we change MTRR entries */
- get_online_cpus();
- mutex_lock(&mtrr_mutex);
- if (reg < 0) {
- /* Search for existing MTRR */
- for (i = 0; i < max; ++i) {
- mtrr_if->get(i, &lbase, &lsize, <ype);
- if (lbase == base && lsize == size) {
- reg = i;
- break;
- }
- }
- if (reg < 0) {
- printk(KERN_DEBUG "mtrr: no MTRR for %lx000,%lx000 found\n", base,
- size);
- goto out;
- }
- }
- if (reg >= max) {
- printk(KERN_WARNING "mtrr: register: %d too big\n", reg);
- goto out;
- }
- mtrr_if->get(reg, &lbase, &lsize, <ype);
- if (lsize < 1) {
- printk(KERN_WARNING "mtrr: MTRR %d not used\n", reg);
- goto out;
- }
- if (mtrr_usage_table[reg] < 1) {
- printk(KERN_WARNING "mtrr: reg: %d has count=0\n", reg);
- goto out;
- }
- if (--mtrr_usage_table[reg] < 1)
- set_mtrr(reg, 0, 0, 0);
- error = reg;
- out:
- mutex_unlock(&mtrr_mutex);
- put_online_cpus();
- return error;
- }
- /**
- * mtrr_del - delete a memory type region
- * @reg: Register returned by mtrr_add
- * @base: Physical base address
- * @size: Size of region
- *
- * If register is supplied then base and size are ignored. This is
- * how drivers should call it.
- *
- * Releases an MTRR region. If the usage count drops to zero the
- * register is freed and the region returns to default state.
- * On success the register is returned, on failure a negative error
- * code.
- */
- int
- mtrr_del(int reg, unsigned long base, unsigned long size)
- {
- if (mtrr_check(base, size))
- return -EINVAL;
- return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);
- }
- EXPORT_SYMBOL(mtrr_add);
- EXPORT_SYMBOL(mtrr_del);
- /* HACK ALERT!
- * These should be called implicitly, but we can't yet until all the initcall
- * stuff is done...
- */
- static void __init init_ifs(void)
- {
- #ifndef CONFIG_X86_64
- amd_init_mtrr();
- cyrix_init_mtrr();
- centaur_init_mtrr();
- #endif
- }
- /* The suspend/resume methods are only for CPU without MTRR. CPU using generic
- * MTRR driver doesn't require this
- */
- struct mtrr_value {
- mtrr_type ltype;
- unsigned long lbase;
- unsigned long lsize;
- };
- static struct mtrr_value mtrr_state[MAX_VAR_RANGES];
- static int mtrr_save(struct sys_device * sysdev, pm_message_t state)
- {
- int i;
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i,
- &mtrr_state[i].lbase,
- &mtrr_state[i].lsize,
- &mtrr_state[i].ltype);
- }
- return 0;
- }
- static int mtrr_restore(struct sys_device * sysdev)
- {
- int i;
- for (i = 0; i < num_var_ranges; i++) {
- if (mtrr_state[i].lsize)
- set_mtrr(i,
- mtrr_state[i].lbase,
- mtrr_state[i].lsize,
- mtrr_state[i].ltype);
- }
- return 0;
- }
- static struct sysdev_driver mtrr_sysdev_driver = {
- .suspend = mtrr_save,
- .resume = mtrr_restore,
- };
- #ifdef CONFIG_MTRR_SANITIZER
- #ifdef CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT
- static int enable_mtrr_cleanup __initdata = 1;
- #else
- static int enable_mtrr_cleanup __initdata;
- #endif
- #else
- static int enable_mtrr_cleanup __initdata = -1;
- #endif
- static int __init disable_mtrr_cleanup_setup(char *str)
- {
- if (enable_mtrr_cleanup != -1)
- enable_mtrr_cleanup = 0;
- return 0;
- }
- early_param("disable_mtrr_cleanup", disable_mtrr_cleanup_setup);
- static int __init enable_mtrr_cleanup_setup(char *str)
- {
- if (enable_mtrr_cleanup != -1)
- enable_mtrr_cleanup = 1;
- return 0;
- }
- early_param("enble_mtrr_cleanup", enable_mtrr_cleanup_setup);
- #define RANGE_NUM 256
- struct res_range {
- unsigned long start;
- unsigned long end;
- };
- static int __init add_range(struct res_range *range, int nr_range, unsigned long start,
- unsigned long end, int merge)
- {
- int i;
- if (!merge)
- goto addit;
- /* try to merge it with old one */
- for (i = 0; i < nr_range; i++) {
- unsigned long final_start, final_end;
- unsigned long common_start, common_end;
- if (!range[i].end)
- continue;
- common_start = max(range[i].start, start);
- common_end = min(range[i].end, end);
- if (common_start > common_end + 1)
- continue;
- final_start = min(range[i].start, start);
- final_end = max(range[i].end, end);
- range[i].start = final_start;
- range[i].end = final_end;
- return nr_range;
- }
- addit:
- /* need to add that */
- if (nr_range >= RANGE_NUM)
- return nr_range;
- range[nr_range].start = start;
- range[nr_range].end = end;
- nr_range++;
- return nr_range;
- }
- static void __init subtract_range(struct res_range *range, unsigned long start,
- unsigned long end)
- {
- int i;
- int j;
- for (j = 0; j < RANGE_NUM; j++) {
- if (!range[j].end)
- continue;
- if (start <= range[j].start && end >= range[j].end) {
- range[j].start = 0;
- range[j].end = 0;
- continue;
- }
- if (start <= range[j].start && end < range[j].end && range[j].start < end + 1) {
- range[j].start = end + 1;
- continue;
- }
- if (start > range[j].start && end >= range[j].end && range[j].end > start - 1) {
- range[j].end = start - 1;
- continue;
- }
- if (start > range[j].start && end < range[j].end) {
- /* find the new spare */
- for (i = 0; i < RANGE_NUM; i++) {
- if (range[i].end == 0)
- break;
- }
- if (i < RANGE_NUM) {
- range[i].end = range[j].end;
- range[i].start = end + 1;
- } else {
- printk(KERN_ERR "run of slot in ranges\n");
- }
- range[j].end = start - 1;
- continue;
- }
- }
- }
- static int __init cmp_range(const void *x1, const void *x2)
- {
- const struct res_range *r1 = x1;
- const struct res_range *r2 = x2;
- long start1, start2;
- start1 = r1->start;
- start2 = r2->start;
- return start1 - start2;
- }
- struct var_mtrr_state {
- unsigned long range_startk, range_sizek;
- unsigned long chunk_sizek;
- unsigned long gran_sizek;
- unsigned int reg;
- unsigned address_bits;
- };
- static void __init set_var_mtrr(
- unsigned int reg, unsigned long basek, unsigned long sizek,
- unsigned char type, unsigned address_bits)
- {
- u32 base_lo, base_hi, mask_lo, mask_hi;
- unsigned address_mask_high;
- if (!sizek) {
- fill_mtrr_var_range(reg, 0, 0, 0, 0);
- return;
- }
- address_mask_high = ((1u << (address_bits - 32u)) - 1u);
- base_hi = basek >> 22;
- base_lo = basek << 10;
- if (sizek < 4*1024*1024) {
- mask_hi = address_mask_high;
- mask_lo = ~((sizek << 10) - 1);
- } else {
- mask_hi = address_mask_high & (~((sizek >> 22) - 1));
- mask_lo = 0;
- }
- base_lo |= type;
- mask_lo |= 0x800;
- fill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi);
- }
- static unsigned int __init range_to_mtrr(unsigned int reg,
- unsigned long range_startk, unsigned long range_sizek,
- unsigned char type, unsigned address_bits)
- {
- if (!range_sizek || (reg >= num_var_ranges))
- return reg;
- while (range_sizek) {
- unsigned long max_align, align;
- unsigned long sizek;
- /* Compute the maximum size I can make a range */
- if (range_startk)
- max_align = ffs(range_startk) - 1;
- else
- max_align = 32;
- align = fls(range_sizek) - 1;
- if (align > max_align)
- align = max_align;
- sizek = 1 << align;
- printk(KERN_INFO "Setting variable MTRR %d, base: %ldMB, range: %ldMB, type %s\n",
- reg, range_startk >> 10, sizek >> 10,
- (type == MTRR_TYPE_UNCACHABLE)?"UC":
- ((type == MTRR_TYPE_WRBACK)?"WB":"Other")
- );
- set_var_mtrr(reg++, range_startk, sizek, type, address_bits);
- range_startk += sizek;
- range_sizek -= sizek;
- if (reg >= num_var_ranges)
- break;
- }
- return reg;
- }
- static void __init range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek)
- {
- unsigned long hole_basek, hole_sizek;
- unsigned long range0_basek, range0_sizek;
- unsigned long range_basek, range_sizek;
- unsigned long chunk_sizek;
- unsigned long gran_sizek;
- hole_basek = 0;
- hole_sizek = 0;
- chunk_sizek = state->chunk_sizek;
- gran_sizek = state->gran_sizek;
- /* align with gran size, prevent small block used up MTRRs */
- range_basek = ALIGN(state->range_startk, gran_sizek);
- if ((range_basek > basek) && basek)
- return;
- range_sizek = ALIGN(state->range_sizek - (range_basek - state->range_startk), gran_sizek);
- while (range_basek + range_sizek > (state->range_startk + state->range_sizek)) {
- range_sizek -= gran_sizek;
- if (!range_sizek)
- return;
- }
- state->range_startk = range_basek;
- state->range_sizek = range_sizek;
- /* try to append some small hole */
- range0_basek = state->range_startk;
- range0_sizek = ALIGN(state->range_sizek, chunk_sizek);
- if ((range0_sizek == state->range_sizek) ||
- ((range0_basek + range0_sizek - chunk_sizek > basek) && basek)) {
- printk(KERN_INFO "rangeX: %016lx - %016lx\n", range0_basek<<10, (range0_basek + state->range_sizek)<<10);
- state->reg = range_to_mtrr(state->reg, range0_basek,
- state->range_sizek, MTRR_TYPE_WRBACK, state->address_bits);
- return;
- }
- range0_sizek -= chunk_sizek;
- printk(KERN_INFO "range0: %016lx - %016lx\n", range0_basek<<10, (range0_basek + range0_sizek)<<10);
- state->reg = range_to_mtrr(state->reg, range0_basek,
- range0_sizek, MTRR_TYPE_WRBACK, state->address_bits);
- range_basek = range0_basek + range0_sizek;
- range_sizek = chunk_sizek;
- if (range_sizek - (state->range_sizek - range0_sizek) < (chunk_sizek >> 1)) {
- hole_sizek = range_sizek - (state->range_sizek - range0_sizek);
- hole_basek = range_basek + range_sizek - hole_sizek;
- } else
- range_sizek = state->range_sizek - range0_sizek;
- printk(KERN_INFO "range: %016lx - %016lx\n", range_basek<<10, (range_basek + range_sizek)<<10);
- state->reg = range_to_mtrr(state->reg, range_basek,
- range_sizek, MTRR_TYPE_WRBACK, state->address_bits);
- if (hole_sizek) {
- printk(KERN_INFO "hole: %016lx - %016lx\n", hole_basek<<10, (hole_basek + hole_sizek)<<10);
- state->reg = range_to_mtrr(state->reg, hole_basek,
- hole_sizek, MTRR_TYPE_UNCACHABLE, state->address_bits);
- }
- }
- static void __init set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn, unsigned long size_pfn)
- {
- unsigned long basek, sizek;
- if (state->reg >= num_var_ranges)
- return;
- basek = base_pfn << (PAGE_SHIFT - 10);
- sizek = size_pfn << (PAGE_SHIFT - 10);
- /* See if I can merge with the last range */
- if ((basek <= 1024) || (state->range_startk + state->range_sizek == basek)) {
- unsigned long endk = basek + sizek;
- state->range_sizek = endk - state->range_startk;
- return;
- }
- /* Write the range mtrrs */
- if (state->range_sizek != 0) {
- range_to_mtrr_with_hole(state, basek);
- state->range_startk = 0;
- state->range_sizek = 0;
- }
- /* Allocate an msr */
- state->range_startk = basek;
- state->range_sizek = sizek;
- }
- /* mininum size of mtrr block that can take hole */
- static u64 mtrr_chunk_size __initdata = (256ULL<<20);
- static int __init parse_mtrr_chunk_size_opt(char *p)
- {
- if (!p)
- return -EINVAL;
- mtrr_chunk_size = memparse(p, &p);
- return 0;
- }
- early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt);
- /* granity of mtrr of block */
- static u64 mtrr_gran_size __initdata = (64ULL<<20);
- static int __init parse_mtrr_gran_size_opt(char *p)
- {
- if (!p)
- return -EINVAL;
- mtrr_gran_size = memparse(p, &p);
- return 0;
- }
- early_param("mtrr_gran_size", parse_mtrr_gran_size_opt);
- static void __init x86_setup_var_mtrrs(struct res_range *range, int nr_range, unsigned address_bits)
- {
- struct var_mtrr_state var_state;
- int i;
- var_state.range_startk = 0;
- var_state.range_sizek = 0;
- var_state.reg = 0;
- var_state.address_bits = address_bits;
- var_state.chunk_sizek = mtrr_chunk_size >> 10;
- var_state.gran_sizek = mtrr_gran_size >> 10;
- /* Write the range etc */
- for (i = 0; i < nr_range; i++)
- set_var_mtrr_range(&var_state, range[i].start, range[i].end - range[i].start + 1);
- /* Write the last range */
- range_to_mtrr_with_hole(&var_state, 0);
- printk(KERN_INFO "DONE variable MTRRs\n");
- /* Clear out the extra MTRR's */
- while (var_state.reg < num_var_ranges)
- set_var_mtrr(var_state.reg++, 0, 0, 0, var_state.address_bits);
- }
- static int __init x86_get_mtrr_mem_range(struct res_range *range, int nr_range, unsigned long extra_remove_base, unsigned long extra_remove_size)
- {
- unsigned long i, base, size;
- mtrr_type type;
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
- if (type != MTRR_TYPE_WRBACK)
- continue;
- nr_range = add_range(range, nr_range, base, base + size - 1, 1);
- }
- printk(KERN_INFO "After WB checking\n");
- for (i = 0; i < nr_range; i++)
- printk(KERN_INFO "MTRR MAP PFN: %016lx - %016lx\n", range[i].start, range[i].end + 1);
- /* take out UC ranges */
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
- if (type != MTRR_TYPE_UNCACHABLE)
- continue;
- if (!size)
- continue;
- subtract_range(range, base, base + size - 1);
- }
- if (extra_remove_size)
- subtract_range(range, extra_remove_base, extra_remove_base + extra_remove_size - 1);
- /* get new range num */
- nr_range = 0;
- for (i = 0; i < RANGE_NUM; i++) {
- if (!range[i].end)
- continue;
- nr_range++;
- }
- printk(KERN_INFO "After UC checking\n");
- for (i = 0; i < nr_range; i++)
- printk(KERN_INFO "MTRR MAP PFN: %016lx - %016lx\n", range[i].start, range[i].end + 1);
- /* sort the ranges */
- sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL);
- printk(KERN_INFO "After sorting\n");
- for (i = 0; i < nr_range; i++)
- printk(KERN_INFO "MTRR MAP PFN: %016lx - %016lx\n", range[i].start, range[i].end + 1);
- return nr_range;
- }
- static int __init mtrr_cleanup(unsigned address_bits)
- {
- unsigned long i, base, size, def, dummy;
- mtrr_type type;
- struct res_range range[RANGE_NUM];
- int nr_range;
- unsigned long extra_remove_base, extra_remove_size;
- /* extra one for all 0 */
- int num[MTRR_NUM_TYPES + 1];
- if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
- return 0;
- rdmsr(MTRRdefType_MSR, def, dummy);
- def &= 0xff;
- if (def != MTRR_TYPE_UNCACHABLE)
- return 0;
- /* check entries number */
- memset(num, 0, sizeof(num));
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
- if (type >= MTRR_NUM_TYPES)
- continue;
- if (!size)
- type = MTRR_NUM_TYPES;
- num[type]++;
- }
- /* check if we got UC entries */
- if (!num[MTRR_TYPE_UNCACHABLE])
- return 0;
- /* check if we only had WB and UC */
- if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
- num_var_ranges - num[MTRR_NUM_TYPES])
- return 0;
- memset(range, 0, sizeof(range));
- extra_remove_size = 0;
- if (mtrr_tom2) {
- extra_remove_base = 1 << (32 - PAGE_SHIFT);
- extra_remove_size = (mtrr_tom2>>PAGE_SHIFT) - extra_remove_base;
- }
- nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base, extra_remove_size);
- /* convert ranges to var ranges state */
- x86_setup_var_mtrrs(range, nr_range, address_bits);
- return 1;
- }
- static int disable_mtrr_trim;
- static int __init disable_mtrr_trim_setup(char *str)
- {
- disable_mtrr_trim = 1;
- return 0;
- }
- early_param("disable_mtrr_trim", disable_mtrr_trim_setup);
- /*
- * Newer AMD K8s and later CPUs have a special magic MSR way to force WB
- * for memory >4GB. Check for that here.
- * Note this won't check if the MTRRs < 4GB where the magic bit doesn't
- * apply to are wrong, but so far we don't know of any such case in the wild.
- */
- #define Tom2Enabled (1U << 21)
- #define Tom2ForceMemTypeWB (1U << 22)
- int __init amd_special_default_mtrr(void)
- {
- u32 l, h;
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
- return 0;
- if (boot_cpu_data.x86 < 0xf || boot_cpu_data.x86 > 0x11)
- return 0;
- /* In case some hypervisor doesn't pass SYSCFG through */
- if (rdmsr_safe(MSR_K8_SYSCFG, &l, &h) < 0)
- return 0;
- /*
- * Memory between 4GB and top of mem is forced WB by this magic bit.
- * Reserved before K8RevF, but should be zero there.
- */
- if ((l & (Tom2Enabled | Tom2ForceMemTypeWB)) ==
- (Tom2Enabled | Tom2ForceMemTypeWB))
- return 1;
- return 0;
- }
- /**
- * mtrr_trim_uncached_memory - trim RAM not covered by MTRRs
- * @end_pfn: ending page frame number
- *
- * Some buggy BIOSes don't setup the MTRRs properly for systems with certain
- * memory configurations. This routine checks that the highest MTRR matches
- * the end of memory, to make sure the MTRRs having a write back type cover
- * all of the memory the kernel is intending to use. If not, it'll trim any
- * memory off the end by adjusting end_pfn, removing it from the kernel's
- * allocation pools, warning the user with an obnoxious message.
- */
- int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
- {
- unsigned long i, base, size, highest_pfn = 0, def, dummy;
- mtrr_type type;
- u64 trim_start, trim_size;
- /*
- * Make sure we only trim uncachable memory on machines that
- * support the Intel MTRR architecture:
- */
- if (!is_cpu(INTEL) || disable_mtrr_trim)
- return 0;
- rdmsr(MTRRdefType_MSR, def, dummy);
- def &= 0xff;
- if (def != MTRR_TYPE_UNCACHABLE)
- return 0;
- if (amd_special_default_mtrr())
- return 0;
- /* Find highest cached pfn */
- for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
- if (type != MTRR_TYPE_WRBACK)
- continue;
- if (highest_pfn < base + size)
- highest_pfn = base + size;
- }
- /* kvm/qemu doesn't have mtrr set right, don't trim them all */
- if (!highest_pfn) {
- if (!kvm_para_available()) {
- printk(KERN_WARNING
- "WARNING: strange, CPU MTRRs all blank?\n");
- WARN_ON(1);
- }
- return 0;
- }
- if (highest_pfn < end_pfn) {
- printk(KERN_WARNING "WARNING: BIOS bug: CPU MTRRs don't cover"
- " all of memory, losing %luMB of RAM.\n",
- (end_pfn - highest_pfn) >> (20 - PAGE_SHIFT));
- WARN_ON(1);
- printk(KERN_INFO "update e820 for mtrr\n");
- trim_start = highest_pfn;
- trim_start <<= PAGE_SHIFT;
- trim_size = end_pfn;
- trim_size <<= PAGE_SHIFT;
- trim_size -= trim_start;
- update_memory_range(trim_start, trim_size, E820_RAM,
- E820_RESERVED);
- update_e820();
- return 1;
- }
- return 0;
- }
- /**
- * mtrr_bp_init - initialize mtrrs on the boot CPU
- *
- * This needs to be called early; before any of the other CPUs are
- * initialized (i.e. before smp_init()).
- *
- */
- void __init mtrr_bp_init(void)
- {
- u32 phys_addr;
- init_ifs();
- phys_addr = 32;
- if (cpu_has_mtrr) {
- mtrr_if = &generic_mtrr_ops;
- size_or_mask = 0xff000000; /* 36 bits */
- size_and_mask = 0x00f00000;
- phys_addr = 36;
- /* This is an AMD specific MSR, but we assume(hope?) that
- Intel will implement it to when they extend the address
- bus of the Xeon. */
- if (cpuid_eax(0x80000000) >= 0x80000008) {
- phys_addr = cpuid_eax(0x80000008) & 0xff;
- /* CPUID workaround for Intel 0F33/0F34 CPU */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
- boot_cpu_data.x86 == 0xF &&
- boot_cpu_data.x86_model == 0x3 &&
- (boot_cpu_data.x86_mask == 0x3 ||
- boot_cpu_data.x86_mask == 0x4))
- phys_addr = 36;
- size_or_mask = ~((1ULL << (phys_addr - PAGE_SHIFT)) - 1);
- size_and_mask = ~size_or_mask & 0xfffff00000ULL;
- } else if (boot_cpu_data.x86_vendor == X86_VENDOR_CENTAUR &&
- boot_cpu_data.x86 == 6) {
- /* VIA C* family have Intel style MTRRs, but
- don't support PAE */
- size_or_mask = 0xfff00000; /* 32 bits */
- size_and_mask = 0;
- phys_addr = 32;
- }
- } else {
- switch (boot_cpu_data.x86_vendor) {
- case X86_VENDOR_AMD:
- if (cpu_has_k6_mtrr) {
- /* Pre-Athlon (K6) AMD CPU MTRRs */
- mtrr_if = mtrr_ops[X86_VENDOR_AMD];
- size_or_mask = 0xfff00000; /* 32 bits */
- size_and_mask = 0;
- }
- break;
- case X86_VENDOR_CENTAUR:
- if (cpu_has_centaur_mcr) {
- mtrr_if = mtrr_ops[X86_VENDOR_CENTAUR];
- size_or_mask = 0xfff00000; /* 32 bits */
- size_and_mask = 0;
- }
- break;
- case X86_VENDOR_CYRIX:
- if (cpu_has_cyrix_arr) {
- mtrr_if = mtrr_ops[X86_VENDOR_CYRIX];
- size_or_mask = 0xfff00000; /* 32 bits */
- size_and_mask = 0;
- }
- break;
- default:
- break;
- }
- }
- if (mtrr_if) {
- set_num_var_ranges();
- init_table();
- if (use_intel()) {
- get_mtrr_state();
- if (mtrr_cleanup(phys_addr))
- mtrr_if->set_all();
- }
- }
- }
- void mtrr_ap_init(void)
- {
- unsigned long flags;
- if (!mtrr_if || !use_intel())
- return;
- /*
- * Ideally we should hold mtrr_mutex here to avoid mtrr entries changed,
- * but this routine will be called in cpu boot time, holding the lock
- * breaks it. This routine is called in two cases: 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);
- mtrr_if->set_all();
- local_irq_restore(flags);
- }
- /**
- * Save current fixed-range MTRR state of the BSP
- */
- void mtrr_save_state(void)
- {
- smp_call_function_single(0, mtrr_save_fixed_ranges, NULL, 1, 1);
- }
- static int __init mtrr_init_finialize(void)
- {
- if (!mtrr_if)
- return 0;
- if (use_intel()) {
- if (enable_mtrr_cleanup < 1)
- mtrr_state_warn();
- } else {
- /* The CPUs haven't MTRR and seem to not support SMP. They have
- * specific drivers, we use a tricky method to support
- * suspend/resume for them.
- * TBD: is there any system with such CPU which supports
- * suspend/resume? if no, we should remove the code.
- */
- sysdev_driver_register(&cpu_sysdev_class,
- &mtrr_sysdev_driver);
- }
- return 0;
- }
- subsys_initcall(mtrr_init_finialize);
|