x86_mem.c revision 185341
145405Smsmith/*- 245405Smsmith * Copyright (c) 1999 Michael Smith <msmith@freebsd.org> 345405Smsmith * All rights reserved. 445405Smsmith * 545405Smsmith * Redistribution and use in source and binary forms, with or without 645405Smsmith * modification, are permitted provided that the following conditions 745405Smsmith * are met: 845405Smsmith * 1. Redistributions of source code must retain the above copyright 945405Smsmith * notice, this list of conditions and the following disclaimer. 1045405Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1145405Smsmith * notice, this list of conditions and the following disclaimer in the 1245405Smsmith * documentation and/or other materials provided with the distribution. 1345405Smsmith * 1445405Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1545405Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1645405Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1745405Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1845405Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1945405Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2045405Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2145405Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2245405Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2345405Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2445405Smsmith * SUCH DAMAGE. 2545405Smsmith */ 2645405Smsmith 27115683Sobrien#include <sys/cdefs.h> 28115683Sobrien__FBSDID("$FreeBSD: head/sys/i386/i386/i686_mem.c 185341 2008-11-26 19:25:13Z jkim $"); 29115683Sobrien 3045405Smsmith#include <sys/param.h> 3145405Smsmith#include <sys/kernel.h> 3245405Smsmith#include <sys/systm.h> 3345405Smsmith#include <sys/malloc.h> 3445405Smsmith#include <sys/memrange.h> 3576078Sjhb#include <sys/smp.h> 36106842Smdodd#include <sys/sysctl.h> 3745405Smsmith 38185341Sjkim#include <machine/cputypes.h> 3945405Smsmith#include <machine/md_var.h> 4045405Smsmith#include <machine/specialreg.h> 4145405Smsmith 4245405Smsmith/* 4345405Smsmith * i686 memory range operations 4445405Smsmith * 4545405Smsmith * This code will probably be impenetrable without reference to the 4645405Smsmith * Intel Pentium Pro documentation. 4745405Smsmith */ 4845405Smsmith 4945405Smsmithstatic char *mem_owner_bios = "BIOS"; 5045405Smsmith 51177070Sjhb#define MR686_FIXMTRR (1<<0) 5245405Smsmith 53177070Sjhb#define mrwithin(mr, a) \ 54177070Sjhb (((a) >= (mr)->mr_base) && ((a) < ((mr)->mr_base + (mr)->mr_len))) 55177070Sjhb#define mroverlap(mra, mrb) \ 56177070Sjhb (mrwithin(mra, mrb->mr_base) || mrwithin(mrb, mra->mr_base)) 5745405Smsmith 58177070Sjhb#define mrvalid(base, len) \ 59177070Sjhb ((!(base & ((1 << 12) - 1))) && /* base is multiple of 4k */ \ 60177070Sjhb ((len) >= (1 << 12)) && /* length is >= 4k */ \ 61177070Sjhb powerof2((len)) && /* ... and power of two */ \ 62177070Sjhb !((base) & ((len) - 1))) /* range is not discontiuous */ 6345405Smsmith 64177070Sjhb#define mrcopyflags(curr, new) \ 65177070Sjhb (((curr) & ~MDF_ATTRMASK) | ((new) & MDF_ATTRMASK)) 6645405Smsmith 67177070Sjhbstatic int mtrrs_disabled; 68106842SmdoddTUNABLE_INT("machdep.disable_mtrrs", &mtrrs_disabled); 69121307SsilbySYSCTL_INT(_machdep, OID_AUTO, disable_mtrrs, CTLFLAG_RDTUN, 70177070Sjhb &mtrrs_disabled, 0, "Disable i686 MTRRs."); 71106842Smdodd 72177070Sjhbstatic void i686_mrinit(struct mem_range_softc *sc); 73177070Sjhbstatic int i686_mrset(struct mem_range_softc *sc, 74177070Sjhb struct mem_range_desc *mrd, int *arg); 75177070Sjhbstatic void i686_mrAPinit(struct mem_range_softc *sc); 7645405Smsmith 7745405Smsmithstatic struct mem_range_ops i686_mrops = { 78177070Sjhb i686_mrinit, 79177070Sjhb i686_mrset, 80177070Sjhb i686_mrAPinit 8145405Smsmith}; 8245405Smsmith 8346215Smsmith/* XXX for AP startup hook */ 84177070Sjhbstatic u_int64_t mtrrcap, mtrrdef; 8546215Smsmith 86177125Sjhb/* The bitmask for the PhysBase and PhysMask fields of the variable MTRRs. */ 87177125Sjhbstatic u_int64_t mtrr_physmask; 88177125Sjhb 89177070Sjhbstatic struct mem_range_desc *mem_range_match(struct mem_range_softc *sc, 90177070Sjhb struct mem_range_desc *mrd); 91177070Sjhbstatic void i686_mrfetch(struct mem_range_softc *sc); 92177070Sjhbstatic int i686_mtrrtype(int flags); 93177070Sjhbstatic int i686_mrt2mtrr(int flags, int oldval); 94177070Sjhbstatic int i686_mtrrconflict(int flag1, int flag2); 95177070Sjhbstatic void i686_mrstore(struct mem_range_softc *sc); 96177070Sjhbstatic void i686_mrstoreone(void *arg); 97177070Sjhbstatic struct mem_range_desc *i686_mtrrfixsearch(struct mem_range_softc *sc, 98177070Sjhb u_int64_t addr); 99177070Sjhbstatic int i686_mrsetlow(struct mem_range_softc *sc, 100177070Sjhb struct mem_range_desc *mrd, int *arg); 101177070Sjhbstatic int i686_mrsetvariable(struct mem_range_softc *sc, 102177070Sjhb struct mem_range_desc *mrd, int *arg); 10345405Smsmith 10445405Smsmith/* i686 MTRR type to memory range type conversion */ 10545405Smsmithstatic int i686_mtrrtomrt[] = { 106177070Sjhb MDF_UNCACHEABLE, 107177070Sjhb MDF_WRITECOMBINE, 108177070Sjhb MDF_UNKNOWN, 109177070Sjhb MDF_UNKNOWN, 110177070Sjhb MDF_WRITETHROUGH, 111177070Sjhb MDF_WRITEPROTECT, 112177070Sjhb MDF_WRITEBACK 11345405Smsmith}; 11445405Smsmith 115177070Sjhb#define MTRRTOMRTLEN (sizeof(i686_mtrrtomrt) / sizeof(i686_mtrrtomrt[0])) 11694683Sdwmalone 11794683Sdwmalonestatic int 118177070Sjhbi686_mtrr2mrt(int val) 119177070Sjhb{ 120177070Sjhb 12194683Sdwmalone if (val < 0 || val >= MTRRTOMRTLEN) 122177070Sjhb return (MDF_UNKNOWN); 123177070Sjhb return (i686_mtrrtomrt[val]); 12494683Sdwmalone} 12594683Sdwmalone 126177070Sjhb/* 12794683Sdwmalone * i686 MTRR conflicts. Writeback and uncachable may overlap. 12848925Smsmith */ 12994683Sdwmalonestatic int 130177070Sjhbi686_mtrrconflict(int flag1, int flag2) 131177070Sjhb{ 132177070Sjhb 13394683Sdwmalone flag1 &= MDF_ATTRMASK; 13494683Sdwmalone flag2 &= MDF_ATTRMASK; 13594683Sdwmalone if (flag1 == flag2 || 13694683Sdwmalone (flag1 == MDF_WRITEBACK && flag2 == MDF_UNCACHEABLE) || 13794683Sdwmalone (flag2 == MDF_WRITEBACK && flag1 == MDF_UNCACHEABLE)) 138177070Sjhb return (0); 139177070Sjhb return (1); 14094683Sdwmalone} 14145405Smsmith 14245405Smsmith/* 14345405Smsmith * Look for an exactly-matching range. 14445405Smsmith */ 14545405Smsmithstatic struct mem_range_desc * 146177070Sjhbmem_range_match(struct mem_range_softc *sc, struct mem_range_desc *mrd) 14745405Smsmith{ 148177070Sjhb struct mem_range_desc *cand; 149177070Sjhb int i; 150177070Sjhb 151177070Sjhb for (i = 0, cand = sc->mr_desc; i < sc->mr_ndesc; i++, cand++) 152177070Sjhb if ((cand->mr_base == mrd->mr_base) && 153177070Sjhb (cand->mr_len == mrd->mr_len)) 154177070Sjhb return (cand); 155177070Sjhb return (NULL); 15645405Smsmith} 15745405Smsmith 15845405Smsmith/* 159177070Sjhb * Fetch the current mtrr settings from the current CPU (assumed to 160177070Sjhb * all be in sync in the SMP case). Note that if we are here, we 161177070Sjhb * assume that MTRRs are enabled, and we may or may not have fixed 162177070Sjhb * MTRRs. 16345405Smsmith */ 16445405Smsmithstatic void 16545405Smsmithi686_mrfetch(struct mem_range_softc *sc) 16645405Smsmith{ 167177070Sjhb struct mem_range_desc *mrd; 168177070Sjhb u_int64_t msrv; 169177070Sjhb int i, j, msr; 17045405Smsmith 171177070Sjhb mrd = sc->mr_desc; 17245405Smsmith 173177070Sjhb /* Get fixed-range MTRRs. */ 174177070Sjhb if (sc->mr_cap & MR686_FIXMTRR) { 175177070Sjhb msr = MSR_MTRR64kBase; 176177070Sjhb for (i = 0; i < (MTRR_N64K / 8); i++, msr++) { 177177070Sjhb msrv = rdmsr(msr); 178177070Sjhb for (j = 0; j < 8; j++, mrd++) { 179177070Sjhb mrd->mr_flags = 180177070Sjhb (mrd->mr_flags & ~MDF_ATTRMASK) | 181177070Sjhb i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 182177070Sjhb if (mrd->mr_owner[0] == 0) 183177070Sjhb strcpy(mrd->mr_owner, mem_owner_bios); 184177070Sjhb msrv = msrv >> 8; 185177070Sjhb } 186177070Sjhb } 187177070Sjhb msr = MSR_MTRR16kBase; 188177070Sjhb for (i = 0; i < (MTRR_N16K / 8); i++, msr++) { 189177070Sjhb msrv = rdmsr(msr); 190177070Sjhb for (j = 0; j < 8; j++, mrd++) { 191177070Sjhb mrd->mr_flags = 192177070Sjhb (mrd->mr_flags & ~MDF_ATTRMASK) | 193177070Sjhb i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 194177070Sjhb if (mrd->mr_owner[0] == 0) 195177070Sjhb strcpy(mrd->mr_owner, mem_owner_bios); 196177070Sjhb msrv = msrv >> 8; 197177070Sjhb } 198177070Sjhb } 199177070Sjhb msr = MSR_MTRR4kBase; 200177070Sjhb for (i = 0; i < (MTRR_N4K / 8); i++, msr++) { 201177070Sjhb msrv = rdmsr(msr); 202177070Sjhb for (j = 0; j < 8; j++, mrd++) { 203177070Sjhb mrd->mr_flags = 204177070Sjhb (mrd->mr_flags & ~MDF_ATTRMASK) | 205177070Sjhb i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; 206177070Sjhb if (mrd->mr_owner[0] == 0) 207177070Sjhb strcpy(mrd->mr_owner, mem_owner_bios); 208177070Sjhb msrv = msrv >> 8; 209177070Sjhb } 210177070Sjhb } 21145405Smsmith } 212177070Sjhb 213177070Sjhb /* Get remainder which must be variable MTRRs. */ 214177070Sjhb msr = MSR_MTRRVarBase; 215177070Sjhb for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { 216177070Sjhb msrv = rdmsr(msr); 21745405Smsmith mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | 218177070Sjhb i686_mtrr2mrt(msrv & MTRR_PHYSBASE_TYPE); 219177125Sjhb mrd->mr_base = msrv & mtrr_physmask; 220177070Sjhb msrv = rdmsr(msr + 1); 221177070Sjhb mrd->mr_flags = (msrv & MTRR_PHYSMASK_VALID) ? 222177070Sjhb (mrd->mr_flags | MDF_ACTIVE) : 223177070Sjhb (mrd->mr_flags & ~MDF_ACTIVE); 224177070Sjhb 225177070Sjhb /* Compute the range from the mask. Ick. */ 226177125Sjhb mrd->mr_len = (~(msrv & mtrr_physmask) & 227177125Sjhb (mtrr_physmask | 0xfffLL)) + 1; 228177070Sjhb if (!mrvalid(mrd->mr_base, mrd->mr_len)) 229177070Sjhb mrd->mr_flags |= MDF_BOGUS; 230177070Sjhb 231177070Sjhb /* If unclaimed and active, must be the BIOS. */ 232177070Sjhb if ((mrd->mr_flags & MDF_ACTIVE) && (mrd->mr_owner[0] == 0)) 233177070Sjhb strcpy(mrd->mr_owner, mem_owner_bios); 23445405Smsmith } 23545405Smsmith} 23645405Smsmith 23745405Smsmith/* 23845405Smsmith * Return the MTRR memory type matching a region's flags 23945405Smsmith */ 24045405Smsmithstatic int 24145405Smsmithi686_mtrrtype(int flags) 24245405Smsmith{ 243177070Sjhb int i; 24445405Smsmith 245177070Sjhb flags &= MDF_ATTRMASK; 24645405Smsmith 247177070Sjhb for (i = 0; i < MTRRTOMRTLEN; i++) { 248177070Sjhb if (i686_mtrrtomrt[i] == MDF_UNKNOWN) 249177070Sjhb continue; 250177070Sjhb if (flags == i686_mtrrtomrt[i]) 251177070Sjhb return (i); 252177070Sjhb } 253177070Sjhb return (-1); 25445405Smsmith} 25545405Smsmith 25694683Sdwmalonestatic int 25794683Sdwmalonei686_mrt2mtrr(int flags, int oldval) 25894683Sdwmalone{ 25994683Sdwmalone int val; 26094683Sdwmalone 26194683Sdwmalone if ((val = i686_mtrrtype(flags)) == -1) 262177070Sjhb return (oldval & 0xff); 263177070Sjhb return (val & 0xff); 26494683Sdwmalone} 26594683Sdwmalone 26645405Smsmith/* 26746215Smsmith * Update running CPU(s) MTRRs to match the ranges in the descriptor 26846215Smsmith * list. 26946215Smsmith * 27046215Smsmith * XXX Must be called with interrupts enabled. 27145405Smsmith */ 27248925Smsmithstatic void 27345405Smsmithi686_mrstore(struct mem_range_softc *sc) 27445405Smsmith{ 27545405Smsmith#ifdef SMP 276177070Sjhb /* 277177070Sjhb * We should use ipi_all_but_self() to call other CPUs into a 278177070Sjhb * locking gate, then call a target function to do this work. 279177070Sjhb * The "proper" solution involves a generalised locking gate 280177070Sjhb * implementation, not ready yet. 281177070Sjhb */ 282177070Sjhb smp_rendezvous(NULL, i686_mrstoreone, NULL, sc); 28348925Smsmith#else 284177070Sjhb disable_intr(); /* disable interrupts */ 285177070Sjhb i686_mrstoreone(sc); 286177070Sjhb enable_intr(); 28748925Smsmith#endif 28846215Smsmith} 28946215Smsmith 29046215Smsmith/* 29146215Smsmith * Update the current CPU's MTRRs with those represented in the 292177070Sjhb * descriptor list. Note that we do this wholesale rather than just 293177070Sjhb * stuffing one entry; this is simpler (but slower, of course). 29446215Smsmith */ 29548925Smsmithstatic void 29648925Smsmithi686_mrstoreone(void *arg) 29746215Smsmith{ 298177070Sjhb struct mem_range_softc *sc = arg; 299177070Sjhb struct mem_range_desc *mrd; 300177070Sjhb u_int64_t omsrv, msrv; 301177070Sjhb int i, j, msr; 302177070Sjhb u_int cr4save; 30346215Smsmith 304177070Sjhb mrd = sc->mr_desc; 30546215Smsmith 306177070Sjhb /* Disable PGE. */ 307177070Sjhb cr4save = rcr4(); 308177070Sjhb if (cr4save & CR4_PGE) 309177070Sjhb load_cr4(cr4save & ~CR4_PGE); 31045405Smsmith 311177070Sjhb /* Disable caches (CD = 1, NW = 0). */ 312177070Sjhb load_cr0((rcr0() & ~CR0_NW) | CR0_CD); 313177070Sjhb 314177070Sjhb /* Flushes caches and TLBs. */ 315177070Sjhb wbinvd(); 316177070Sjhb 317177070Sjhb /* Disable MTRRs (E = 0). */ 318177070Sjhb wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) & ~MTRR_DEF_ENABLE); 319177070Sjhb 320177070Sjhb /* Set fixed-range MTRRs. */ 321177070Sjhb if (sc->mr_cap & MR686_FIXMTRR) { 322177070Sjhb msr = MSR_MTRR64kBase; 323177070Sjhb for (i = 0; i < (MTRR_N64K / 8); i++, msr++) { 324177070Sjhb msrv = 0; 325177070Sjhb omsrv = rdmsr(msr); 326177070Sjhb for (j = 7; j >= 0; j--) { 327177070Sjhb msrv = msrv << 8; 328177070Sjhb msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 329177070Sjhb omsrv >> (j * 8)); 330177070Sjhb } 331177070Sjhb wrmsr(msr, msrv); 332177070Sjhb mrd += 8; 333177070Sjhb } 334177070Sjhb msr = MSR_MTRR16kBase; 335177070Sjhb for (i = 0; i < (MTRR_N16K / 8); i++, msr++) { 336177070Sjhb msrv = 0; 337177070Sjhb omsrv = rdmsr(msr); 338177070Sjhb for (j = 7; j >= 0; j--) { 339177070Sjhb msrv = msrv << 8; 340177070Sjhb msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 341177070Sjhb omsrv >> (j * 8)); 342177070Sjhb } 343177070Sjhb wrmsr(msr, msrv); 344177070Sjhb mrd += 8; 345177070Sjhb } 346177070Sjhb msr = MSR_MTRR4kBase; 347177070Sjhb for (i = 0; i < (MTRR_N4K / 8); i++, msr++) { 348177070Sjhb msrv = 0; 349177070Sjhb omsrv = rdmsr(msr); 350177070Sjhb for (j = 7; j >= 0; j--) { 351177070Sjhb msrv = msrv << 8; 352177070Sjhb msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, 353177070Sjhb omsrv >> (j * 8)); 354177070Sjhb } 355177070Sjhb wrmsr(msr, msrv); 356177070Sjhb mrd += 8; 357177070Sjhb } 35845405Smsmith } 359177070Sjhb 360177070Sjhb /* Set remainder which must be variable MTRRs. */ 361177070Sjhb msr = MSR_MTRRVarBase; 362177070Sjhb for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { 363177070Sjhb /* base/type register */ 364177070Sjhb omsrv = rdmsr(msr); 365177070Sjhb if (mrd->mr_flags & MDF_ACTIVE) { 366177125Sjhb msrv = mrd->mr_base & mtrr_physmask; 367177070Sjhb msrv |= i686_mrt2mtrr(mrd->mr_flags, omsrv); 368177070Sjhb } else { 369177070Sjhb msrv = 0; 370177070Sjhb } 371177070Sjhb wrmsr(msr, msrv); 372177070Sjhb 373177070Sjhb /* mask/active register */ 374177070Sjhb if (mrd->mr_flags & MDF_ACTIVE) { 375177070Sjhb msrv = MTRR_PHYSMASK_VALID | 376177125Sjhb (~(mrd->mr_len - 1) & mtrr_physmask); 377177070Sjhb } else { 378177070Sjhb msrv = 0; 379177070Sjhb } 380177070Sjhb wrmsr(msr + 1, msrv); 38145405Smsmith } 38245405Smsmith 383177070Sjhb /* Flush caches, TLBs. */ 384177070Sjhb wbinvd(); 385177070Sjhb 386177070Sjhb /* Enable MTRRs. */ 387177070Sjhb wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) | MTRR_DEF_ENABLE); 388177070Sjhb 389177070Sjhb /* Enable caches (CD = 0, NW = 0). */ 390177070Sjhb load_cr0(rcr0() & ~(CR0_CD | CR0_NW)); 391177070Sjhb 392177070Sjhb /* Restore PGE. */ 393177070Sjhb load_cr4(cr4save); 39445405Smsmith} 39545405Smsmith 39645405Smsmith/* 39745405Smsmith * Hunt for the fixed MTRR referencing (addr) 39845405Smsmith */ 39945405Smsmithstatic struct mem_range_desc * 40045405Smsmithi686_mtrrfixsearch(struct mem_range_softc *sc, u_int64_t addr) 40145405Smsmith{ 402177070Sjhb struct mem_range_desc *mrd; 403177070Sjhb int i; 404177070Sjhb 405177070Sjhb for (i = 0, mrd = sc->mr_desc; i < (MTRR_N64K + MTRR_N16K + MTRR_N4K); 406177070Sjhb i++, mrd++) 407177070Sjhb if ((addr >= mrd->mr_base) && 408177070Sjhb (addr < (mrd->mr_base + mrd->mr_len))) 409177070Sjhb return (mrd); 410177070Sjhb return (NULL); 41145405Smsmith} 41245405Smsmith 41345405Smsmith/* 414177070Sjhb * Try to satisfy the given range request by manipulating the fixed 415177070Sjhb * MTRRs that cover low memory. 41645405Smsmith * 417177070Sjhb * Note that we try to be generous here; we'll bloat the range out to 418177070Sjhb * the next higher/lower boundary to avoid the consumer having to know 419177070Sjhb * too much about the mechanisms here. 42045405Smsmith * 421177070Sjhb * XXX note that this will have to be updated when we start supporting 422177070Sjhb * "busy" ranges. 42345405Smsmith */ 42445405Smsmithstatic int 42545405Smsmithi686_mrsetlow(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg) 42645405Smsmith{ 427177070Sjhb struct mem_range_desc *first_md, *last_md, *curr_md; 42845405Smsmith 429177070Sjhb /* Range check. */ 430177070Sjhb if (((first_md = i686_mtrrfixsearch(sc, mrd->mr_base)) == NULL) || 431177070Sjhb ((last_md = i686_mtrrfixsearch(sc, mrd->mr_base + mrd->mr_len - 1)) == NULL)) 432177070Sjhb return (EINVAL); 43345405Smsmith 434177070Sjhb /* Check that we aren't doing something risky. */ 435177070Sjhb if (!(mrd->mr_flags & MDF_FORCE)) 436177070Sjhb for (curr_md = first_md; curr_md <= last_md; curr_md++) { 437177070Sjhb if ((curr_md->mr_flags & MDF_ATTRMASK) == MDF_UNKNOWN) 438177070Sjhb return (EACCES); 439177070Sjhb } 440177070Sjhb 441177070Sjhb /* Set flags, clear set-by-firmware flag. */ 442103346Sdwmalone for (curr_md = first_md; curr_md <= last_md; curr_md++) { 443177070Sjhb curr_md->mr_flags = mrcopyflags(curr_md->mr_flags & 444177070Sjhb ~MDF_FIRMWARE, mrd->mr_flags); 445177070Sjhb bcopy(mrd->mr_owner, curr_md->mr_owner, sizeof(mrd->mr_owner)); 446103346Sdwmalone } 447103346Sdwmalone 448177070Sjhb return (0); 44945405Smsmith} 45045405Smsmith 45145405Smsmith/* 45245405Smsmith * Modify/add a variable MTRR to satisfy the request. 45345405Smsmith * 45445405Smsmith * XXX needs to be updated to properly support "busy" ranges. 45545405Smsmith */ 45645405Smsmithstatic int 457177070Sjhbi686_mrsetvariable(struct mem_range_softc *sc, struct mem_range_desc *mrd, 458177070Sjhb int *arg) 45945405Smsmith{ 460177070Sjhb struct mem_range_desc *curr_md, *free_md; 461177070Sjhb int i; 462177070Sjhb 463177070Sjhb /* 464177070Sjhb * Scan the currently active variable descriptors, look for 465177070Sjhb * one we exactly match (straight takeover) and for possible 466177070Sjhb * accidental overlaps. 467177070Sjhb * 468177070Sjhb * Keep track of the first empty variable descriptor in case 469177070Sjhb * we can't perform a takeover. 470177070Sjhb */ 471177070Sjhb i = (sc->mr_cap & MR686_FIXMTRR) ? MTRR_N64K + MTRR_N16K + MTRR_N4K : 0; 472177070Sjhb curr_md = sc->mr_desc + i; 473177070Sjhb free_md = NULL; 474177070Sjhb for (; i < sc->mr_ndesc; i++, curr_md++) { 475177070Sjhb if (curr_md->mr_flags & MDF_ACTIVE) { 476177070Sjhb /* Exact match? */ 477177070Sjhb if ((curr_md->mr_base == mrd->mr_base) && 478177070Sjhb (curr_md->mr_len == mrd->mr_len)) { 479177070Sjhb 480177070Sjhb /* Whoops, owned by someone. */ 481177070Sjhb if (curr_md->mr_flags & MDF_BUSY) 482177070Sjhb return (EBUSY); 483177070Sjhb 484177070Sjhb /* Check that we aren't doing something risky */ 485177070Sjhb if (!(mrd->mr_flags & MDF_FORCE) && 486177070Sjhb ((curr_md->mr_flags & MDF_ATTRMASK) == 487177070Sjhb MDF_UNKNOWN)) 488177070Sjhb return (EACCES); 489177070Sjhb 490177070Sjhb /* Ok, just hijack this entry. */ 491177070Sjhb free_md = curr_md; 492177070Sjhb break; 493177070Sjhb } 494177070Sjhb 495177070Sjhb /* Non-exact overlap? */ 496177070Sjhb if (mroverlap(curr_md, mrd)) { 497177070Sjhb /* Between conflicting region types? */ 498177070Sjhb if (i686_mtrrconflict(curr_md->mr_flags, 499177070Sjhb mrd->mr_flags)) 500177070Sjhb return (EINVAL); 501177070Sjhb } 502177070Sjhb } else if (free_md == NULL) { 503177070Sjhb free_md = curr_md; 504177070Sjhb } 50545405Smsmith } 50645405Smsmith 507177070Sjhb /* Got somewhere to put it? */ 508177070Sjhb if (free_md == NULL) 509177070Sjhb return (ENOSPC); 510177070Sjhb 511177070Sjhb /* Set up new descriptor. */ 512177070Sjhb free_md->mr_base = mrd->mr_base; 513177070Sjhb free_md->mr_len = mrd->mr_len; 514177070Sjhb free_md->mr_flags = mrcopyflags(MDF_ACTIVE, mrd->mr_flags); 515177070Sjhb bcopy(mrd->mr_owner, free_md->mr_owner, sizeof(mrd->mr_owner)); 516177070Sjhb return (0); 51745405Smsmith} 51845405Smsmith 51945405Smsmith/* 52045405Smsmith * Handle requests to set memory range attributes by manipulating MTRRs. 52145405Smsmith */ 52245405Smsmithstatic int 52345405Smsmithi686_mrset(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg) 52445405Smsmith{ 525177070Sjhb struct mem_range_desc *targ; 526177070Sjhb int error = 0; 52745405Smsmith 528177070Sjhb switch(*arg) { 529177070Sjhb case MEMRANGE_SET_UPDATE: 530177070Sjhb /* 531177070Sjhb * Make sure that what's being asked for is even 532177070Sjhb * possible at all. 533177070Sjhb */ 534177070Sjhb if (!mrvalid(mrd->mr_base, mrd->mr_len) || 535177070Sjhb i686_mtrrtype(mrd->mr_flags) == -1) 536177070Sjhb return (EINVAL); 53745405Smsmith 538177070Sjhb#define FIXTOP ((MTRR_N64K * 0x10000) + (MTRR_N16K * 0x4000) + (MTRR_N4K * 0x1000)) 53945405Smsmith 540177070Sjhb /* Are the "low memory" conditions applicable? */ 541177070Sjhb if ((sc->mr_cap & MR686_FIXMTRR) && 542177070Sjhb ((mrd->mr_base + mrd->mr_len) <= FIXTOP)) { 543177070Sjhb if ((error = i686_mrsetlow(sc, mrd, arg)) != 0) 544177070Sjhb return (error); 545177070Sjhb } else { 546177070Sjhb /* It's time to play with variable MTRRs. */ 547177070Sjhb if ((error = i686_mrsetvariable(sc, mrd, arg)) != 0) 548177070Sjhb return (error); 549177070Sjhb } 550177070Sjhb break; 551177070Sjhb 552177070Sjhb case MEMRANGE_SET_REMOVE: 553177070Sjhb if ((targ = mem_range_match(sc, mrd)) == NULL) 554177070Sjhb return (ENOENT); 555177070Sjhb if (targ->mr_flags & MDF_FIXACTIVE) 556177070Sjhb return (EPERM); 557177070Sjhb if (targ->mr_flags & MDF_BUSY) 558177070Sjhb return (EBUSY); 559177070Sjhb targ->mr_flags &= ~MDF_ACTIVE; 560177070Sjhb targ->mr_owner[0] = 0; 561177070Sjhb break; 562177070Sjhb 563177070Sjhb default: 564177070Sjhb return (EOPNOTSUPP); 56545405Smsmith } 56645405Smsmith 567177070Sjhb /* Update the hardware. */ 568177070Sjhb i686_mrstore(sc); 56945405Smsmith 570177070Sjhb /* Refetch to see where we're at. */ 571177070Sjhb i686_mrfetch(sc); 572177070Sjhb return (0); 57345405Smsmith} 57445405Smsmith 57545405Smsmith/* 576177070Sjhb * Work out how many ranges we support, initialise storage for them, 577177070Sjhb * and fetch the initial settings. 57845405Smsmith */ 57945405Smsmithstatic void 58045405Smsmithi686_mrinit(struct mem_range_softc *sc) 58145405Smsmith{ 582177070Sjhb struct mem_range_desc *mrd; 583177125Sjhb u_int regs[4]; 584177125Sjhb int i, nmdesc = 0, pabits; 58545405Smsmith 586177070Sjhb mtrrcap = rdmsr(MSR_MTRRcap); 587177070Sjhb mtrrdef = rdmsr(MSR_MTRRdefType); 58845405Smsmith 589177070Sjhb /* For now, bail out if MTRRs are not enabled. */ 590177070Sjhb if (!(mtrrdef & MTRR_DEF_ENABLE)) { 591177070Sjhb if (bootverbose) 592177070Sjhb printf("CPU supports MTRRs but not enabled\n"); 593177070Sjhb return; 594177070Sjhb } 595177070Sjhb nmdesc = mtrrcap & MTRR_CAP_VCNT; 59645405Smsmith if (bootverbose) 597177070Sjhb printf("Pentium Pro MTRR support enabled\n"); 59845405Smsmith 599177125Sjhb /* 600177125Sjhb * Determine the size of the PhysMask and PhysBase fields in 601177125Sjhb * the variable range MTRRs. If the extended CPUID 0x80000008 602177125Sjhb * is present, use that to figure out how many physical 603177125Sjhb * address bits the CPU supports. Otherwise, default to 36 604177125Sjhb * address bits. 605177125Sjhb */ 606177125Sjhb if (cpu_exthigh >= 0x80000008) { 607177125Sjhb do_cpuid(0x80000008, regs); 608177125Sjhb pabits = regs[0] & 0xff; 609177125Sjhb } else 610177125Sjhb pabits = 36; 611177125Sjhb mtrr_physmask = ((1ULL << pabits) - 1) & ~0xfffULL; 612177125Sjhb 613177070Sjhb /* If fixed MTRRs supported and enabled. */ 614177070Sjhb if ((mtrrcap & MTRR_CAP_FIXED) && (mtrrdef & MTRR_DEF_FIXED_ENABLE)) { 615177070Sjhb sc->mr_cap = MR686_FIXMTRR; 616177070Sjhb nmdesc += MTRR_N64K + MTRR_N16K + MTRR_N4K; 617177070Sjhb } 61845405Smsmith 619177070Sjhb sc->mr_desc = malloc(nmdesc * sizeof(struct mem_range_desc), M_MEMDESC, 620177070Sjhb M_WAITOK | M_ZERO); 621177070Sjhb sc->mr_ndesc = nmdesc; 62245405Smsmith 623177070Sjhb mrd = sc->mr_desc; 62445405Smsmith 625177070Sjhb /* Populate the fixed MTRR entries' base/length. */ 626177070Sjhb if (sc->mr_cap & MR686_FIXMTRR) { 627177070Sjhb for (i = 0; i < MTRR_N64K; i++, mrd++) { 628177070Sjhb mrd->mr_base = i * 0x10000; 629177070Sjhb mrd->mr_len = 0x10000; 630177070Sjhb mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 631177070Sjhb MDF_FIXACTIVE; 632177070Sjhb } 633177070Sjhb for (i = 0; i < MTRR_N16K; i++, mrd++) { 634177070Sjhb mrd->mr_base = i * 0x4000 + 0x80000; 635177070Sjhb mrd->mr_len = 0x4000; 636177070Sjhb mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 637177070Sjhb MDF_FIXACTIVE; 638177070Sjhb } 639177070Sjhb for (i = 0; i < MTRR_N4K; i++, mrd++) { 640177070Sjhb mrd->mr_base = i * 0x1000 + 0xc0000; 641177070Sjhb mrd->mr_len = 0x1000; 642177070Sjhb mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | 643177070Sjhb MDF_FIXACTIVE; 644177070Sjhb } 64545405Smsmith } 646177070Sjhb 647177070Sjhb /* 648177070Sjhb * Get current settings, anything set now is considered to 649177070Sjhb * have been set by the firmware. (XXX has something already 650177070Sjhb * played here?) 651177070Sjhb */ 652177070Sjhb i686_mrfetch(sc); 653177070Sjhb mrd = sc->mr_desc; 654177070Sjhb for (i = 0; i < sc->mr_ndesc; i++, mrd++) { 655177070Sjhb if (mrd->mr_flags & MDF_ACTIVE) 656177070Sjhb mrd->mr_flags |= MDF_FIRMWARE; 65745405Smsmith } 65845405Smsmith} 65945405Smsmith 66046215Smsmith/* 66146215Smsmith * Initialise MTRRs on an AP after the BSP has run the init code. 66246215Smsmith */ 66345405Smsmithstatic void 66446215Smsmithi686_mrAPinit(struct mem_range_softc *sc) 66546215Smsmith{ 666177070Sjhb 667177070Sjhb i686_mrstoreone(sc); 668177070Sjhb wrmsr(MSR_MTRRdefType, mtrrdef); 66946215Smsmith} 67046215Smsmith 67146215Smsmithstatic void 67245405Smsmithi686_mem_drvinit(void *unused) 67345405Smsmith{ 674177070Sjhb 675177124Sjhb if (mtrrs_disabled) 676177124Sjhb return; 677177124Sjhb if (!(cpu_feature & CPUID_MTRR)) 678177124Sjhb return; 679177124Sjhb if ((cpu_id & 0xf00) != 0x600 && (cpu_id & 0xf00) != 0xf00) 680177124Sjhb return; 681185341Sjkim if (cpu_vendor_id != CPU_VENDOR_INTEL && 682185341Sjkim cpu_vendor_id != CPU_VENDOR_AMD) 683177124Sjhb return; 684177124Sjhb mem_range_softc.mr_op = &i686_mrops; 68545405Smsmith} 686177070SjhbSYSINIT(i686memdev, SI_SUB_DRIVERS, SI_ORDER_FIRST, i686_mem_drvinit, NULL); 687