diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/i386/i386/i686_mem.c | 105 |
1 files changed, 73 insertions, 32 deletions
diff --git a/sys/arch/i386/i386/i686_mem.c b/sys/arch/i386/i386/i686_mem.c index be862ff0e90..d17d8dcbb2c 100644 --- a/sys/arch/i386/i386/i686_mem.c +++ b/sys/arch/i386/i386/i686_mem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: i686_mem.c,v 1.4 2002/03/14 01:26:32 millert Exp $ */ +/* $OpenBSD: i686_mem.c,v 1.5 2002/10/14 21:01:01 matthieu Exp $ */ /*- * Copyright (c) 1999 Michael Smith <msmith@freebsd.org> * All rights reserved. @@ -79,6 +79,9 @@ struct mem_range_desc *mem_range_match(struct mem_range_softc *sc, struct mem_range_desc *mrd); void i686_mrfetch(struct mem_range_softc *sc); int i686_mtrrtype(int flags); +int i686_mrt2mtrr(int flags, int oldval); +int i686_mtrr2mrt(int val); +int i686_mtrrconflict(int flag1, int flag2); void i686_mrstore(struct mem_range_softc *sc); void i686_mrstoreone(void *arg); struct mem_range_desc *i686_mtrrfixsearch(struct mem_range_softc *sc, @@ -94,29 +97,38 @@ int i686_mrsetvariable(struct mem_range_softc *sc, int i686_mtrrtomrt[] = { MDF_UNCACHEABLE, MDF_WRITECOMBINE, - 0, - 0, + MDF_UNKNOWN, + MDF_UNKNOWN, MDF_WRITETHROUGH, MDF_WRITEPROTECT, MDF_WRITEBACK }; -/* - * i686 MTRR conflict matrix for overlapping ranges - * - * Specifically, this matrix allows writeback and uncached ranges - * to overlap (the overlapped region is uncached). The array index - * is the translated i686 code for the flags (because they map well). +#define MTRRTOMRTLEN (sizeof(i686_mtrrtomrt) / sizeof(i686_mtrrtomrt[0])) + +int +i686_mtrr2mrt(int val) +{ + if (val < 0 || val >= MTRRTOMRTLEN) + return MDF_UNKNOWN; + return i686_mtrrtomrt[val]; +} + +/* + * i686 MTRR conflicts. Writeback and uncachable may overlap. */ -int i686_mtrrconflict[] = { - MDF_WRITECOMBINE | MDF_WRITETHROUGH | MDF_WRITEPROTECT, - MDF_ATTRMASK, - 0, - 0, - MDF_ATTRMASK, - MDF_ATTRMASK, - MDF_WRITECOMBINE | MDF_WRITETHROUGH | MDF_WRITEPROTECT -}; +int +i686_mtrrconflict(int flag1, int flag2) +{ + + flag1 &= MDF_ATTRMASK; + flag2 &= MDF_ATTRMASK; + if (flag1 == flag2 || + (flag1 == MDF_WRITEBACK && flag2 == MDF_UNCACHEABLE) || + (flag2 == MDF_WRITEBACK && flag1 == MDF_UNCACHEABLE)) + return 0; + return 1; +} /* * Look for an exactly-matching range. @@ -158,7 +170,7 @@ i686_mrfetch(sc) msrv = rdmsr(msr); for (j = 0; j < 8; j++, mrd++) { mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | - i686_mtrrtomrt[msrv & 0xff] | + i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; if (mrd->mr_owner[0] == 0) strcpy(mrd->mr_owner, mem_owner_bios); @@ -170,7 +182,7 @@ i686_mrfetch(sc) msrv = rdmsr(msr); for (j = 0; j < 8; j++, mrd++) { mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | - i686_mtrrtomrt[msrv & 0xff] | + i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; if (mrd->mr_owner[0] == 0) strcpy(mrd->mr_owner, mem_owner_bios); @@ -182,7 +194,7 @@ i686_mrfetch(sc) msrv = rdmsr(msr); for (j = 0; j < 8; j++, mrd++) { mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | - i686_mtrrtomrt[msrv & 0xff] | + i686_mtrr2mrt(msrv & 0xff) | MDF_ACTIVE; if (mrd->mr_owner[0] == 0) strcpy(mrd->mr_owner, mem_owner_bios); @@ -196,7 +208,7 @@ i686_mrfetch(sc) for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { msrv = rdmsr(msr); mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) | - i686_mtrrtomrt[msrv & 0xff]; + i686_mtrr2mrt(msrv & 0xff); mrd->mr_base = msrv & 0x0000000ffffff000LL; msrv = rdmsr(msr + 1); mrd->mr_flags = (msrv & 0x800) ? @@ -223,8 +235,8 @@ i686_mtrrtype(flags) flags &= MDF_ATTRMASK; - for (i = 0; i < (sizeof(i686_mtrrtomrt) / sizeof(i686_mtrrtomrt[0])); i++) { - if (i686_mtrrtomrt[i] == 0) + for (i = 0; i < MTRRTOMRTLEN; i++) { + if (i686_mtrrtomrt[i] == MDF_UNKNOWN) continue; if (flags == i686_mtrrtomrt[i]) return(i); @@ -232,6 +244,16 @@ i686_mtrrtype(flags) return(-1); } +int +i686_mrt2mtrr(int flags, int oldval) +{ + int val; + + if ((val = i686_mtrrtype(flags)) == -1) + return oldval & 0xff; + return val & 0xff; +} + /* * Update running CPU(s) MTRRs to match the ranges in the descriptor * list. @@ -258,7 +280,7 @@ i686_mrstoreone(arg) { struct mem_range_softc *sc = (struct mem_range_softc *)arg; struct mem_range_desc *mrd; - u_int64_t msrv; + u_int64_t omsrv, msrv; int i, j, msr; u_int cr4save; @@ -276,9 +298,11 @@ i686_mrstoreone(arg) msr = MSR_MTRR64kBase; for (i = 0; i < (MTRR_N64K / 8); i++, msr++) { msrv = 0; + omsrv = rdmsr(msr); for (j = 7; j >= 0; j--) { msrv = msrv << 8; - msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff); + msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, + omsrv >> (j*8)); } wrmsr(msr, msrv); mrd += 8; @@ -286,9 +310,11 @@ i686_mrstoreone(arg) msr = MSR_MTRR16kBase; for (i = 0; i < (MTRR_N16K / 8); i++, msr++) { msrv = 0; + omsrv = rdmsr(msr); for (j = 7; j >= 0; j--) { msrv = msrv << 8; - msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff); + msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, + omsrv >> (j*8)); } wrmsr(msr, msrv); mrd += 8; @@ -296,9 +322,11 @@ i686_mrstoreone(arg) msr = MSR_MTRR4kBase; for (i = 0; i < (MTRR_N4K / 8); i++, msr++) { msrv = 0; + omsrv = rdmsr(msr); for (j = 7; j >= 0; j--) { msrv = msrv << 8; - msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff); + msrv |= i686_mrt2mtrr((mrd + j)->mr_flags, + omsrv >> (j*8)); } wrmsr(msr, msrv); mrd += 8; @@ -309,9 +337,10 @@ i686_mrstoreone(arg) msr = MSR_MTRRVarBase; for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) { /* base/type register */ + omsrv = rdmsr(msr); if (mrd->mr_flags & MDF_ACTIVE) { msrv = mrd->mr_base & 0x0000000ffffff000LL; - msrv |= (i686_mtrrtype(mrd->mr_flags) & 0xff); + msrv |= i686_mrt2mtrr(mrd->mr_flags, omsrv); } else { msrv = 0; } @@ -371,6 +400,13 @@ i686_mrsetlow(sc, mrd, arg) ((last_md = i686_mtrrfixsearch(sc, mrd->mr_base + mrd->mr_len - 1)) == NULL)) return(EINVAL); + /* check we aren't doing something risky */ + if (!(mrd->mr_flags & MDF_FORCE)) + for (curr_md = first_md; curr_md <= last_md; curr_md++) { + if ((curr_md->mr_flags & MDF_ATTRMASK) == MDF_UNKNOWN) + return (EACCES); + } + /* set flags, clear set-by-firmware flag */ for (curr_md = first_md; curr_md <= last_md; curr_md++) { curr_md->mr_flags = mrcopyflags(curr_md->mr_flags & ~MDF_FIRMWARE, mrd->mr_flags); @@ -413,6 +449,11 @@ i686_mrsetvariable(sc, mrd, arg) /* whoops, owned by someone */ if (curr_md->mr_flags & MDF_BUSY) return(EBUSY); + /* check we aren't doing something risky */ + if (!(mrd->mr_flags & MDF_FORCE) && + ((curr_md->mr_flags & MDF_ATTRMASK) + == MDF_UNKNOWN)) + return (EACCES); /* Ok, just hijack this entry */ free_md = curr_md; break; @@ -420,8 +461,8 @@ i686_mrsetvariable(sc, mrd, arg) /* non-exact overlap ? */ if (mroverlap(curr_md, mrd)) { /* between conflicting region types? */ - if ((i686_mtrrconflict[i686_mtrrtype(curr_md->mr_flags)] & mrd->mr_flags) || - (i686_mtrrconflict[i686_mtrrtype(mrd->mr_flags)] & curr_md->mr_flags)) + if (i686_mtrrconflict(curr_md->mr_flags, + mrd->mr_flags)) return(EINVAL); } } else if (free_md == NULL) { @@ -457,7 +498,7 @@ i686_mrset(sc, mrd, arg) case MEMRANGE_SET_UPDATE: /* make sure that what's being asked for is even possible at all */ if (!mrvalid(mrd->mr_base, mrd->mr_len) || - (i686_mtrrtype(mrd->mr_flags & MDF_ATTRMASK) == -1)) + i686_mtrrtype(mrd->mr_flags) == -1) return(EINVAL); #define FIXTOP ((MTRR_N64K * 0x10000) + (MTRR_N16K * 0x4000) + (MTRR_N4K * 0x1000)) |