x86_mem.c revision 177124
185587Sobrien/*- 285587Sobrien * Copyright (c) 1999 Michael Smith <msmith@freebsd.org> 385587Sobrien * All rights reserved. 485587Sobrien * 585587Sobrien * Redistribution and use in source and binary forms, with or without 685587Sobrien * modification, are permitted provided that the following conditions 785587Sobrien * are met: 885587Sobrien * 1. Redistributions of source code must retain the above copyright 985587Sobrien * notice, this list of conditions and the following disclaimer. 1085587Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1185587Sobrien * notice, this list of conditions and the following disclaimer in the 1285587Sobrien * documentation and/or other materials provided with the distribution. 1385587Sobrien * 1485587Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1585587Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1685587Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1785587Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1885587Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1985587Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2085587Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2185587Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2285587Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2385587Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2485587Sobrien * SUCH DAMAGE. 2585587Sobrien */ 2685587Sobrien 2785587Sobrien#include <sys/cdefs.h> 2885587Sobrien__FBSDID("$FreeBSD: head/sys/i386/i386/i686_mem.c 177124 2008-03-12 21:44:46Z jhb $"); 2985587Sobrien 3085587Sobrien#include <sys/param.h> 3185587Sobrien#include <sys/kernel.h> 3285587Sobrien#include <sys/systm.h> 3385587Sobrien#include <sys/malloc.h> 3485587Sobrien#include <sys/memrange.h> 3585587Sobrien#include <sys/smp.h> 3685587Sobrien#include <sys/sysctl.h> 3785587Sobrien 3885587Sobrien#include <machine/md_var.h> 3985587Sobrien#include <machine/specialreg.h> 4085587Sobrien 4185587Sobrien/* 4285587Sobrien * i686 memory range operations 4385587Sobrien * 4485587Sobrien * This code will probably be impenetrable without reference to the 4585587Sobrien * Intel Pentium Pro documentation. 4685587Sobrien */ 4785587Sobrien 4885587Sobrienstatic char *mem_owner_bios = "BIOS"; 4985587Sobrien 5085587Sobrien#define MR686_FIXMTRR (1<<0) 5185587Sobrien 5285587Sobrien#define mrwithin(mr, a) \ 5385587Sobrien (((a) >= (mr)->mr_base) && ((a) < ((mr)->mr_base + (mr)->mr_len))) 5485587Sobrien#define mroverlap(mra, mrb) \ 5585587Sobrien (mrwithin(mra, mrb->mr_base) || mrwithin(mrb, mra->mr_base)) 5685587Sobrien 5785587Sobrien#define mrvalid(base, len) \ 5885587Sobrien ((!(base & ((1 << 12) - 1))) && /* base is multiple of 4k */ \ 5985587Sobrien ((len) >= (1 << 12)) && /* length is >= 4k */ \ 6085587Sobrien powerof2((len)) && /* ... and power of two */ \ 6185587Sobrien !((base) & ((len) - 1))) /* range is not discontiuous */ 6285587Sobrien 6385587Sobrien#define mrcopyflags(curr, new) \ 6485587Sobrien (((curr) & ~MDF_ATTRMASK) | ((new) & MDF_ATTRMASK)) 6585587Sobrien 6685587Sobrienstatic int mtrrs_disabled; 6785587SobrienTUNABLE_INT("machdep.disable_mtrrs", &mtrrs_disabled); 6885587SobrienSYSCTL_INT(_machdep, OID_AUTO, disable_mtrrs, CTLFLAG_RDTUN, 6985587Sobrien &mtrrs_disabled, 0, "Disable i686 MTRRs."); 7085587Sobrien 7185587Sobrienstatic void i686_mrinit(struct mem_range_softc *sc); 7285587Sobrienstatic int i686_mrset(struct mem_range_softc *sc, 7385587Sobrien struct mem_range_desc *mrd, int *arg); 7485587Sobrienstatic void i686_mrAPinit(struct mem_range_softc *sc); 7585587Sobrien 7685587Sobrienstatic struct mem_range_ops i686_mrops = { 7785587Sobrien i686_mrinit, 78107806Sobrien i686_mrset, 7985587Sobrien i686_mrAPinit 8085587Sobrien}; 8185587Sobrien 8285587Sobrien/* XXX for AP startup hook */ 8385587Sobrienstatic u_int64_t mtrrcap, mtrrdef; 8485587Sobrien 8585587Sobrienstatic struct mem_range_desc *mem_range_match(struct mem_range_softc *sc, 8685587Sobrien struct mem_range_desc *mrd); 8785587Sobrienstatic void i686_mrfetch(struct mem_range_softc *sc); 8885587Sobrienstatic int i686_mtrrtype(int flags); 8985587Sobrienstatic int i686_mrt2mtrr(int flags, int oldval); 9085587Sobrienstatic int i686_mtrrconflict(int flag1, int flag2); 9185587Sobrienstatic void i686_mrstore(struct mem_range_softc *sc); 9285587Sobrienstatic void i686_mrstoreone(void *arg); 9385587Sobrienstatic struct mem_range_desc *i686_mtrrfixsearch(struct mem_range_softc *sc, 9485587Sobrien u_int64_t addr); 9585587Sobrienstatic int i686_mrsetlow(struct mem_range_softc *sc, 9690902Sdes struct mem_range_desc *mrd, int *arg); 9785587Sobrienstatic int i686_mrsetvariable(struct mem_range_softc *sc, 9885587Sobrien struct mem_range_desc *mrd, int *arg); 9985587Sobrien 10085587Sobrien/* i686 MTRR type to memory range type conversion */ 10185587Sobrienstatic int i686_mtrrtomrt[] = { 10285587Sobrien MDF_UNCACHEABLE, 10385587Sobrien MDF_WRITECOMBINE, 10485587Sobrien MDF_UNKNOWN, 10585587Sobrien MDF_UNKNOWN, 10685587Sobrien MDF_WRITETHROUGH, 10785587Sobrien MDF_WRITEPROTECT, 10885587Sobrien MDF_WRITEBACK 10985587Sobrien}; 11085587Sobrien 11185587Sobrien#define MTRRTOMRTLEN (sizeof(i686_mtrrtomrt) / sizeof(i686_mtrrtomrt[0])) 11285587Sobrien 11385587Sobrienstatic int 11485587Sobrieni686_mtrr2mrt(int val) 11585587Sobrien{ 11685587Sobrien 11785587Sobrien if (val < 0 || val >= MTRRTOMRTLEN) 11885587Sobrien return (MDF_UNKNOWN); 11985587Sobrien return (i686_mtrrtomrt[val]); 120107806Sobrien} 12185587Sobrien 12285587Sobrien/* 12385587Sobrien * i686 MTRR conflicts. Writeback and uncachable may overlap. 12485587Sobrien */ 12585587Sobrienstatic int 12685587Sobrieni686_mtrrconflict(int flag1, int flag2) 12785587Sobrien{ 12885587Sobrien 12985587Sobrien flag1 &= MDF_ATTRMASK; 13085587Sobrien flag2 &= MDF_ATTRMASK; 13185587Sobrien if (flag1 == flag2 || 13285587Sobrien (flag1 == MDF_WRITEBACK && flag2 == MDF_UNCACHEABLE) || 13385587Sobrien (flag2 == MDF_WRITEBACK && flag1 == MDF_UNCACHEABLE)) 13485587Sobrien return (0); 13585587Sobrien return (1); 13685587Sobrien} 13785587Sobrien 13885587Sobrien/* 13985587Sobrien * Look for an exactly-matching range. 14085587Sobrien */ 14185587Sobrienstatic struct mem_range_desc * 14285587Sobrienmem_range_match(struct mem_range_softc *sc, struct mem_range_desc *mrd) 14385587Sobrien{ 14485587Sobrien struct mem_range_desc *cand; 14585587Sobrien int i; 14685587Sobrien 14785587Sobrien for (i = 0, cand = sc->mr_desc; i < sc->mr_ndesc; i++, cand++) 14885587Sobrien if ((cand->mr_base == mrd->mr_base) && 14985587Sobrien (cand->mr_len == mrd->mr_len)) 15085587Sobrien return (cand); 15185587Sobrien return (NULL); 15285587Sobrien} 15385587Sobrien 15485587Sobrien/* 15585587Sobrien * Fetch the current mtrr settings from the current CPU (assumed to 15685587Sobrien * all be in sync in the SMP case). Note that if we are here, we 15785587Sobrien * assume that MTRRs are enabled, and we may or may not have fixed 15885587Sobrien * MTRRs. 15985587Sobrien */ 16085587Sobrienstatic void 16185587Sobrieni686_mrfetch(struct mem_range_softc *sc) 16285587Sobrien{ 16385587Sobrien struct mem_range_desc *mrd; 16485587Sobrien u_int64_t msrv; 16585587Sobrien int i, j, msr; 16685587Sobrien 16785587Sobrien mrd = sc->mr_desc; 16885587Sobrien 16985587Sobrien /* Get fixed-range MTRRs. */ 17085587Sobrien if (sc->mr_cap & MR686_FIXMTRR) { 17185587Sobrien msr = MSR_MTRR64kBase; 17285587Sobrien for (i = 0; i < (MTRR_N64K / 8); i++, msr++) { 17385587Sobrien msrv = rdmsr(msr); 17485587Sobrien for (j = 0; j < 8; j++, mrd++) { 17585587Sobrien mrd->mr_flags = 17685587Sobrien (mrd->mr_flags & ~MDF_ATTRMASK) | 17785587Sobrien i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 17885587Sobrien if (mrd->mr_owner[0] == 0) 17985587Sobrien strcpy(mrd->mr_owner, mem_owner_bios); 18085587Sobrien msrv = msrv >> 8; 18185587Sobrien } 18285587Sobrien } 18385587Sobrien msr = MSR_MTRR16kBase; 18485587Sobrien for (i = 0; i < (MTRR_N16K / 8); i++, msr++) { 18585587Sobrien msrv = rdmsr(msr); 18685587Sobrien for (j = 0; j < 8; j++, mrd++) { 18785587Sobrien mrd->mr_flags = 18885587Sobrien (mrd->mr_flags & ~MDF_ATTRMASK) | 18985587Sobrien i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 19085587Sobrien if (mrd->mr_owner[0] == 0) 19185587Sobrien strcpy(mrd->mr_owner, mem_owner_bios); 19285587Sobrien msrv = msrv >> 8; 19385587Sobrien } 19485587Sobrien } 19585587Sobrien msr = MSR_MTRR4kBase; 19685587Sobrien for (i = 0; i < (MTRR_N4K / 8); i++, msr++) { 19785587Sobrien msrv = rdmsr(msr); 19885587Sobrien for (j = 0; j < 8; j++, mrd++) { 19985587Sobrien mrd->mr_flags = 20085587Sobrien (mrd->mr_flags & ~MDF_ATTRMASK) | 20185587Sobrien i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 20285587Sobrien if (mrd->mr_owner[0] == 0) 20385587Sobrien strcpy(mrd->mr_owner, mem_owner_bios); 20485587Sobrien msrv = msrv >> 8; 20585587Sobrien } 20685587Sobrien } 20785587Sobrien } 20885587Sobrien 20985587Sobrien /* Get remainder which must be variable MTRRs. */ 21085587Sobrien msr = MSR_MTRRVarBase; 21185587Sobrien for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { 21285587Sobrien msrv = rdmsr(msr); 21385587Sobrien mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | 21485587Sobrien i686_mtrr2mrt(msrv & MTRR_PHYSBASE_TYPE); 21585587Sobrien mrd->mr_base = msrv & MTRR_PHYSBASE_PHYSBASE; 21685587Sobrien msrv = rdmsr(msr + 1); 21785587Sobrien mrd->mr_flags = (msrv & MTRR_PHYSMASK_VALID) ? 21885587Sobrien (mrd->mr_flags | MDF_ACTIVE) : 21985587Sobrien (mrd->mr_flags & ~MDF_ACTIVE); 22085587Sobrien 22185587Sobrien /* Compute the range from the mask. Ick. */ 22285587Sobrien mrd->mr_len = (~(msrv & MTRR_PHYSMASK_PHYSMASK) & 22385587Sobrien (MTRR_PHYSMASK_PHYSMASK | 0xfffLL)) + 1; 22485587Sobrien if (!mrvalid(mrd->mr_base, mrd->mr_len)) 22585587Sobrien mrd->mr_flags |= MDF_BOGUS; 22685587Sobrien 22785587Sobrien /* If unclaimed and active, must be the BIOS. */ 22885587Sobrien if ((mrd->mr_flags & MDF_ACTIVE) && (mrd->mr_owner[0] == 0)) 22985587Sobrien strcpy(mrd->mr_owner, mem_owner_bios); 23085587Sobrien } 23185587Sobrien} 23285587Sobrien 23385587Sobrien/* 23485587Sobrien * Return the MTRR memory type matching a region's flags 23585587Sobrien */ 23685587Sobrienstatic int 23785587Sobrieni686_mtrrtype(int flags) 23885587Sobrien{ 23985587Sobrien int i; 24085587Sobrien 24185587Sobrien flags &= MDF_ATTRMASK; 24285587Sobrien 24385587Sobrien for (i = 0; i < MTRRTOMRTLEN; i++) { 24485587Sobrien if (i686_mtrrtomrt[i] == MDF_UNKNOWN) 24585587Sobrien continue; 24685587Sobrien if (flags == i686_mtrrtomrt[i]) 24785587Sobrien return (i); 24885587Sobrien } 24985587Sobrien return (-1); 25085587Sobrien} 25185587Sobrien 25285587Sobrienstatic int 25385587Sobrieni686_mrt2mtrr(int flags, int oldval) 25485587Sobrien{ 25585587Sobrien int val; 25685587Sobrien 25785587Sobrien if ((val = i686_mtrrtype(flags)) == -1) 25885587Sobrien return (oldval & 0xff); 25985587Sobrien return (val & 0xff); 26085587Sobrien} 26185587Sobrien 26285587Sobrien/* 26385587Sobrien * Update running CPU(s) MTRRs to match the ranges in the descriptor 26485587Sobrien * list. 26585587Sobrien * 26685587Sobrien * XXX Must be called with interrupts enabled. 26785587Sobrien */ 26885587Sobrienstatic void 26985587Sobrieni686_mrstore(struct mem_range_softc *sc) 27085587Sobrien{ 27185587Sobrien#ifdef SMP 27285587Sobrien /* 27385587Sobrien * We should use ipi_all_but_self() to call other CPUs into a 27485587Sobrien * locking gate, then call a target function to do this work. 27585587Sobrien * The "proper" solution involves a generalised locking gate 27685587Sobrien * implementation, not ready yet. 27785587Sobrien */ 27885587Sobrien smp_rendezvous(NULL, i686_mrstoreone, NULL, sc); 27985587Sobrien#else 28085587Sobrien disable_intr(); /* disable interrupts */ 28185587Sobrien i686_mrstoreone(sc); 28285587Sobrien enable_intr(); 28385587Sobrien#endif 28485587Sobrien} 285107806Sobrien 28685587Sobrien/* 287107806Sobrien * Update the current CPU's MTRRs with those represented in the 288107806Sobrien * descriptor list. Note that we do this wholesale rather than just 289107806Sobrien * stuffing one entry; this is simpler (but slower, of course). 290107806Sobrien */ 291107806Sobrienstatic void 292107806Sobrieni686_mrstoreone(void *arg) 293107806Sobrien{ 294107806Sobrien struct mem_range_softc *sc = arg; 295107806Sobrien struct mem_range_desc *mrd; 296107806Sobrien u_int64_t omsrv, msrv; 297107806Sobrien int i, j, msr; 298107806Sobrien u_int cr4save; 299107806Sobrien 300107806Sobrien mrd = sc->mr_desc; 30185587Sobrien 302107806Sobrien /* Disable PGE. */ 30385587Sobrien cr4save = rcr4(); 30485587Sobrien if (cr4save & CR4_PGE) 30585587Sobrien load_cr4(cr4save & ~CR4_PGE); 30685587Sobrien 30785587Sobrien /* Disable caches (CD = 1, NW = 0). */ 30885587Sobrien load_cr0((rcr0() & ~CR0_NW) | CR0_CD); 30985587Sobrien 31085587Sobrien /* Flushes caches and TLBs. */ 31185587Sobrien wbinvd(); 31285587Sobrien 31385587Sobrien /* Disable MTRRs (E = 0). */ 31485587Sobrien wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) & ~MTRR_DEF_ENABLE); 31585587Sobrien 31685587Sobrien /* Set fixed-range MTRRs. */ 31785587Sobrien if (sc->mr_cap & MR686_FIXMTRR) { 31885587Sobrien msr = MSR_MTRR64kBase; 31985587Sobrien for (i = 0; i < (MTRR_N64K / 8); i++, msr++) { 32085587Sobrien msrv = 0; 321107806Sobrien omsrv = rdmsr(msr); 32285587Sobrien for (j = 7; j >= 0; j--) { 32385587Sobrien msrv = msrv << 8; 32485587Sobrien msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 32585587Sobrien omsrv >> (j * 8)); 326107806Sobrien } 327107806Sobrien wrmsr(msr, msrv); 328107806Sobrien mrd += 8; 329107806Sobrien } 33085587Sobrien msr = MSR_MTRR16kBase; 33185587Sobrien for (i = 0; i < (MTRR_N16K / 8); i++, msr++) { 332107806Sobrien msrv = 0; 33385587Sobrien omsrv = rdmsr(msr); 33485587Sobrien for (j = 7; j >= 0; j--) { 33585587Sobrien msrv = msrv << 8; 33685587Sobrien msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 33785587Sobrien omsrv >> (j * 8)); 33885587Sobrien } 33985587Sobrien wrmsr(msr, msrv); 34085587Sobrien mrd += 8; 34185587Sobrien } 34285587Sobrien msr = MSR_MTRR4kBase; 34385587Sobrien for (i = 0; i < (MTRR_N4K / 8); i++, msr++) { 34485587Sobrien msrv = 0; 34585587Sobrien omsrv = rdmsr(msr); 34685587Sobrien for (j = 7; j >= 0; j--) { 34785587Sobrien msrv = msrv << 8; 34885587Sobrien msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 349107806Sobrien omsrv >> (j * 8)); 35085587Sobrien } 35185587Sobrien wrmsr(msr, msrv); 35285587Sobrien mrd += 8; 35385587Sobrien } 35485587Sobrien } 35585587Sobrien 35685587Sobrien /* Set remainder which must be variable MTRRs. */ 35785587Sobrien msr = MSR_MTRRVarBase; 35885587Sobrien for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { 35985587Sobrien /* base/type register */ 36085587Sobrien omsrv = rdmsr(msr); 36185587Sobrien if (mrd->mr_flags & MDF_ACTIVE) { 36285587Sobrien msrv = mrd->mr_base & MTRR_PHYSBASE_PHYSBASE; 36385587Sobrien msrv |= i686_mrt2mtrr(mrd->mr_flags, omsrv); 36485587Sobrien } else { 36585587Sobrien msrv = 0; 36685587Sobrien } 36785587Sobrien wrmsr(msr, msrv); 36885587Sobrien 36985587Sobrien /* mask/active register */ 37085587Sobrien if (mrd->mr_flags & MDF_ACTIVE) { 37185587Sobrien msrv = MTRR_PHYSMASK_VALID | 37285587Sobrien (~(mrd->mr_len - 1) & MTRR_PHYSMASK_PHYSMASK); 37385587Sobrien } else { 37485587Sobrien msrv = 0; 37585587Sobrien } 37685587Sobrien wrmsr(msr + 1, msrv); 37785587Sobrien } 37885587Sobrien 37985587Sobrien /* Flush caches, TLBs. */ 38085587Sobrien wbinvd(); 38185587Sobrien 38285587Sobrien /* Enable MTRRs. */ 38385587Sobrien wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) | MTRR_DEF_ENABLE); 38485587Sobrien 38585587Sobrien /* Enable caches (CD = 0, NW = 0). */ 38685587Sobrien load_cr0(rcr0() & ~(CR0_CD | CR0_NW)); 38785587Sobrien 38885587Sobrien /* Restore PGE. */ 38985587Sobrien load_cr4(cr4save); 39085587Sobrien} 39185587Sobrien 39285587Sobrien/* 39385587Sobrien * Hunt for the fixed MTRR referencing (addr) 39485587Sobrien */ 39585587Sobrienstatic struct mem_range_desc * 39685587Sobrieni686_mtrrfixsearch(struct mem_range_softc *sc, u_int64_t addr) 39785587Sobrien{ 39885587Sobrien struct mem_range_desc *mrd; 39985587Sobrien int i; 40085587Sobrien 40185587Sobrien for (i = 0, mrd = sc->mr_desc; i < (MTRR_N64K + MTRR_N16K + MTRR_N4K); 40285587Sobrien i++, mrd++) 40385587Sobrien if ((addr >= mrd->mr_base) && 40485587Sobrien (addr < (mrd->mr_base + mrd->mr_len))) 40585587Sobrien return (mrd); 40685587Sobrien return (NULL); 40785587Sobrien} 40885587Sobrien 40985587Sobrien/* 41085587Sobrien * Try to satisfy the given range request by manipulating the fixed 41185587Sobrien * MTRRs that cover low memory. 41285587Sobrien * 41385587Sobrien * Note that we try to be generous here; we'll bloat the range out to 41485587Sobrien * the next higher/lower boundary to avoid the consumer having to know 41585587Sobrien * too much about the mechanisms here. 41685587Sobrien * 41785587Sobrien * XXX note that this will have to be updated when we start supporting 41885587Sobrien * "busy" ranges. 41985587Sobrien */ 42085587Sobrienstatic int 42185587Sobrieni686_mrsetlow(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg) 42285587Sobrien{ 42385587Sobrien struct mem_range_desc *first_md, *last_md, *curr_md; 42485587Sobrien 42585587Sobrien /* Range check. */ 42685587Sobrien if (((first_md = i686_mtrrfixsearch(sc, mrd->mr_base)) == NULL) || 42785587Sobrien ((last_md = i686_mtrrfixsearch(sc, mrd->mr_base + mrd->mr_len - 1)) == NULL)) 42885587Sobrien return (EINVAL); 42985587Sobrien 43085587Sobrien /* Check that we aren't doing something risky. */ 43185587Sobrien if (!(mrd->mr_flags & MDF_FORCE)) 43285587Sobrien for (curr_md = first_md; curr_md <= last_md; curr_md++) { 43385587Sobrien if ((curr_md->mr_flags & MDF_ATTRMASK) == MDF_UNKNOWN) 43485587Sobrien return (EACCES); 43585587Sobrien } 43685587Sobrien 43785587Sobrien /* Set flags, clear set-by-firmware flag. */ 43885587Sobrien for (curr_md = first_md; curr_md <= last_md; curr_md++) { 43985587Sobrien curr_md->mr_flags = mrcopyflags(curr_md->mr_flags & 44085587Sobrien ~MDF_FIRMWARE, mrd->mr_flags); 44185587Sobrien bcopy(mrd->mr_owner, curr_md->mr_owner, sizeof(mrd->mr_owner)); 44285587Sobrien } 44385587Sobrien 44485587Sobrien return (0); 44585587Sobrien} 44685587Sobrien 44785587Sobrien/* 44885587Sobrien * Modify/add a variable MTRR to satisfy the request. 44985587Sobrien * 45085587Sobrien * XXX needs to be updated to properly support "busy" ranges. 45185587Sobrien */ 45285587Sobrienstatic int 45385587Sobrieni686_mrsetvariable(struct mem_range_softc *sc, struct mem_range_desc *mrd, 45485587Sobrien int *arg) 45585587Sobrien{ 45685587Sobrien struct mem_range_desc *curr_md, *free_md; 45785587Sobrien int i; 45885587Sobrien 45985587Sobrien /* 46085587Sobrien * Scan the currently active variable descriptors, look for 46185587Sobrien * one we exactly match (straight takeover) and for possible 46285587Sobrien * accidental overlaps. 46385587Sobrien * 46485587Sobrien * Keep track of the first empty variable descriptor in case 46585587Sobrien * we can't perform a takeover. 46685587Sobrien */ 467107806Sobrien i = (sc->mr_cap & MR686_FIXMTRR) ? MTRR_N64K + MTRR_N16K + MTRR_N4K : 0; 46885587Sobrien curr_md = sc->mr_desc + i; 46985587Sobrien free_md = NULL; 47085587Sobrien for (; i < sc->mr_ndesc; i++, curr_md++) { 47185587Sobrien if (curr_md->mr_flags & MDF_ACTIVE) { 47285587Sobrien /* Exact match? */ 47385587Sobrien if ((curr_md->mr_base == mrd->mr_base) && 47485587Sobrien (curr_md->mr_len == mrd->mr_len)) { 47585587Sobrien 47685587Sobrien /* Whoops, owned by someone. */ 477107806Sobrien if (curr_md->mr_flags & MDF_BUSY) 47885587Sobrien return (EBUSY); 47985587Sobrien 48085587Sobrien /* Check that we aren't doing something risky */ 48185587Sobrien if (!(mrd->mr_flags & MDF_FORCE) && 48285587Sobrien ((curr_md->mr_flags & MDF_ATTRMASK) == 48385587Sobrien MDF_UNKNOWN)) 48485587Sobrien return (EACCES); 48585587Sobrien 48685587Sobrien /* Ok, just hijack this entry. */ 48785587Sobrien free_md = curr_md; 48885587Sobrien break; 48985587Sobrien } 49085587Sobrien 49185587Sobrien /* Non-exact overlap? */ 49285587Sobrien if (mroverlap(curr_md, mrd)) { 49385587Sobrien /* Between conflicting region types? */ 49485587Sobrien if (i686_mtrrconflict(curr_md->mr_flags, 49585587Sobrien mrd->mr_flags)) 496107806Sobrien return (EINVAL); 49785587Sobrien } 49885587Sobrien } else if (free_md == NULL) { 49985587Sobrien free_md = curr_md; 50085587Sobrien } 50185587Sobrien } 50285587Sobrien 50385587Sobrien /* Got somewhere to put it? */ 50485587Sobrien if (free_md == NULL) 50585587Sobrien return (ENOSPC); 50685587Sobrien 50785587Sobrien /* Set up new descriptor. */ 50885587Sobrien free_md->mr_base = mrd->mr_base; 50985587Sobrien free_md->mr_len = mrd->mr_len; 51085587Sobrien free_md->mr_flags = mrcopyflags(MDF_ACTIVE, mrd->mr_flags); 51185587Sobrien bcopy(mrd->mr_owner, free_md->mr_owner, sizeof(mrd->mr_owner)); 51285587Sobrien return (0); 51385587Sobrien} 51485587Sobrien 51585587Sobrien/* 51685587Sobrien * Handle requests to set memory range attributes by manipulating MTRRs. 51785587Sobrien */ 51885587Sobrienstatic int 51985587Sobrieni686_mrset(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg) 52085587Sobrien{ 52185587Sobrien struct mem_range_desc *targ; 52285587Sobrien int error = 0; 52385587Sobrien 52485587Sobrien switch(*arg) { 52585587Sobrien case MEMRANGE_SET_UPDATE: 52685587Sobrien /* 52785587Sobrien * Make sure that what's being asked for is even 52885587Sobrien * possible at all. 52985587Sobrien */ 53085587Sobrien if (!mrvalid(mrd->mr_base, mrd->mr_len) || 53185587Sobrien i686_mtrrtype(mrd->mr_flags) == -1) 53285587Sobrien return (EINVAL); 53385587Sobrien 53485587Sobrien#define FIXTOP ((MTRR_N64K * 0x10000) + (MTRR_N16K * 0x4000) + (MTRR_N4K * 0x1000)) 53585587Sobrien 53685587Sobrien /* Are the "low memory" conditions applicable? */ 53785587Sobrien if ((sc->mr_cap & MR686_FIXMTRR) && 53885587Sobrien ((mrd->mr_base + mrd->mr_len) <= FIXTOP)) { 53985587Sobrien if ((error = i686_mrsetlow(sc, mrd, arg)) != 0) 54085587Sobrien return (error); 54185587Sobrien } else { 54285587Sobrien /* It's time to play with variable MTRRs. */ 54385587Sobrien if ((error = i686_mrsetvariable(sc, mrd, arg)) != 0) 54485587Sobrien return (error); 54585587Sobrien } 54685587Sobrien break; 54785587Sobrien 54885587Sobrien case MEMRANGE_SET_REMOVE: 549107806Sobrien if ((targ = mem_range_match(sc, mrd)) == NULL) 55085587Sobrien return (ENOENT); 55185587Sobrien if (targ->mr_flags & MDF_FIXACTIVE) 55285587Sobrien return (EPERM); 55385587Sobrien if (targ->mr_flags & MDF_BUSY) 55485587Sobrien return (EBUSY); 55585587Sobrien targ->mr_flags &= ~MDF_ACTIVE; 55685587Sobrien targ->mr_owner[0] = 0; 55785587Sobrien break; 55885587Sobrien 55985587Sobrien default: 56085587Sobrien return (EOPNOTSUPP); 56185587Sobrien } 56285587Sobrien 56385587Sobrien /* Update the hardware. */ 56485587Sobrien i686_mrstore(sc); 56585587Sobrien 56685587Sobrien /* Refetch to see where we're at. */ 56785587Sobrien i686_mrfetch(sc); 56885587Sobrien return (0); 56985587Sobrien} 57085587Sobrien 57185587Sobrien/* 57285587Sobrien * Work out how many ranges we support, initialise storage for them, 57385587Sobrien * and fetch the initial settings. 57485587Sobrien */ 57585587Sobrienstatic void 57685587Sobrieni686_mrinit(struct mem_range_softc *sc) 57785587Sobrien{ 57885587Sobrien struct mem_range_desc *mrd; 57985587Sobrien int i, nmdesc = 0; 58085587Sobrien 58185587Sobrien mtrrcap = rdmsr(MSR_MTRRcap); 58285587Sobrien mtrrdef = rdmsr(MSR_MTRRdefType); 58385587Sobrien 58485587Sobrien /* For now, bail out if MTRRs are not enabled. */ 58585587Sobrien if (!(mtrrdef & MTRR_DEF_ENABLE)) { 58685587Sobrien if (bootverbose) 58785587Sobrien printf("CPU supports MTRRs but not enabled\n"); 58885587Sobrien return; 58985587Sobrien } 59085587Sobrien nmdesc = mtrrcap & MTRR_CAP_VCNT; 59185587Sobrien if (bootverbose) 59285587Sobrien printf("Pentium Pro MTRR support enabled\n"); 59385587Sobrien 59485587Sobrien /* If fixed MTRRs supported and enabled. */ 59585587Sobrien if ((mtrrcap & MTRR_CAP_FIXED) && (mtrrdef & MTRR_DEF_FIXED_ENABLE)) { 59685587Sobrien sc->mr_cap = MR686_FIXMTRR; 59785587Sobrien nmdesc += MTRR_N64K + MTRR_N16K + MTRR_N4K; 59885587Sobrien } 59985587Sobrien 60085587Sobrien sc->mr_desc = malloc(nmdesc * sizeof(struct mem_range_desc), M_MEMDESC, 601107806Sobrien M_WAITOK | M_ZERO); 60285587Sobrien sc->mr_ndesc = nmdesc; 60385587Sobrien 60485587Sobrien mrd = sc->mr_desc; 60585587Sobrien 60685587Sobrien /* Populate the fixed MTRR entries' base/length. */ 60785587Sobrien if (sc->mr_cap & MR686_FIXMTRR) { 608107806Sobrien for (i = 0; i < MTRR_N64K; i++, mrd++) { 60985587Sobrien mrd->mr_base = i * 0x10000; 610107806Sobrien mrd->mr_len = 0x10000; 611107806Sobrien mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 61285587Sobrien MDF_FIXACTIVE; 61385587Sobrien } 61485587Sobrien for (i = 0; i < MTRR_N16K; i++, mrd++) { 61585587Sobrien mrd->mr_base = i * 0x4000 + 0x80000; 61685587Sobrien mrd->mr_len = 0x4000; 61785587Sobrien mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 61885587Sobrien MDF_FIXACTIVE; 61985587Sobrien } 62085587Sobrien for (i = 0; i < MTRR_N4K; i++, mrd++) { 62185587Sobrien mrd->mr_base = i * 0x1000 + 0xc0000; 62285587Sobrien mrd->mr_len = 0x1000; 62385587Sobrien mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 62485587Sobrien MDF_FIXACTIVE; 62585587Sobrien } 62685587Sobrien } 62785587Sobrien 62885587Sobrien /* 62985587Sobrien * Get current settings, anything set now is considered to 63085587Sobrien * have been set by the firmware. (XXX has something already 63185587Sobrien * played here?) 63285587Sobrien */ 63385587Sobrien i686_mrfetch(sc); 63485587Sobrien mrd = sc->mr_desc; 63585587Sobrien for (i = 0; i < sc->mr_ndesc; i++, mrd++) { 63685587Sobrien if (mrd->mr_flags & MDF_ACTIVE) 63785587Sobrien mrd->mr_flags |= MDF_FIRMWARE; 63885587Sobrien } 63985587Sobrien} 64085587Sobrien 64185587Sobrien/* 64285587Sobrien * Initialise MTRRs on an AP after the BSP has run the init code. 64385587Sobrien */ 64485587Sobrienstatic void 64585587Sobrieni686_mrAPinit(struct mem_range_softc *sc) 64685587Sobrien{ 64785587Sobrien 64885587Sobrien i686_mrstoreone(sc); 64985587Sobrien wrmsr(MSR_MTRRdefType, mtrrdef); 65085587Sobrien} 65185587Sobrien 65285587Sobrienstatic void 65385587Sobrieni686_mem_drvinit(void *unused) 65485587Sobrien{ 65585587Sobrien 65685587Sobrien if (mtrrs_disabled) 65785587Sobrien return; 65885587Sobrien if (!(cpu_feature & CPUID_MTRR)) 65985587Sobrien return; 66085587Sobrien if ((cpu_id & 0xf00) != 0x600 && (cpu_id & 0xf00) != 0xf00) 66185587Sobrien return; 66285587Sobrien if ((strcmp(cpu_vendor, "GenuineIntel") != 0) && 66385587Sobrien (strcmp(cpu_vendor, "AuthenticAMD") != 0)) 66485587Sobrien return; 66585587Sobrien mem_range_softc.mr_op = &i686_mrops; 66685587Sobrien} 66785587SobrienSYSINIT(i686memdev, SI_SUB_DRIVERS, SI_ORDER_FIRST, i686_mem_drvinit, NULL); 66885587Sobrien