x86_mem.c revision 49421
1/*-
2 * Copyright (c) 1999 Michael Smith <msmith@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id: i686_mem.c,v 1.4 1999/07/20 06:58:51 msmith Exp $
27 */
28
29#include "opt_smp.h"
30
31#include <sys/param.h>
32#include <sys/kernel.h>
33#include <sys/systm.h>
34#include <sys/ioccom.h>
35#include <sys/malloc.h>
36#include <sys/memrange.h>
37
38#include <machine/md_var.h>
39#include <machine/specialreg.h>
40
41#ifdef SMP
42#include "machine/smp.h"
43#endif
44
45/*
46 * i686 memory range operations
47 *
48 * This code will probably be impenetrable without reference to the
49 * Intel Pentium Pro documentation.
50 */
51
52static char *mem_owner_bios = "BIOS";
53
54#define MR686_FIXMTRR	(1<<0)
55
56#define mrwithin(mr, a) \
57    (((a) >= (mr)->mr_base) && ((a) < ((mr)->mr_base + (mr)->mr_len)))
58#define mroverlap(mra, mrb) \
59    (mrwithin(mra, mrb->mr_base) || mrwithin(mrb, mra->mr_base))
60
61#define mrvalid(base, len) 						\
62    ((!(base & ((1 << 12) - 1))) && 	/* base is multiple of 4k */	\
63     ((len) >= (1 << 12)) && 		/* length is >= 4k */		\
64     powerof2((len)) && 		/* ... and power of two */	\
65     !((base) & ((len) - 1)))		/* range is not discontiuous */
66
67#define mrcopyflags(curr, new) (((curr) & ~MDF_ATTRMASK) | ((new) & MDF_ATTRMASK))
68
69static void			i686_mrinit(struct mem_range_softc *sc);
70static int			i686_mrset(struct mem_range_softc *sc,
71					   struct mem_range_desc *mrd,
72					   int *arg);
73static void			i686_mrAPinit(struct mem_range_softc *sc);
74
75static struct mem_range_ops i686_mrops = {
76    i686_mrinit,
77    i686_mrset,
78    i686_mrAPinit
79};
80
81/* XXX for AP startup hook */
82static u_int64_t		mtrrcap, mtrrdef;
83
84static struct mem_range_desc	*mem_range_match(struct mem_range_softc *sc,
85						 struct mem_range_desc *mrd);
86static void			i686_mrfetch(struct mem_range_softc *sc);
87static int			i686_mtrrtype(int flags);
88static void			i686_mrstore(struct mem_range_softc *sc);
89static void			i686_mrstoreone(void *arg);
90static struct mem_range_desc	*i686_mtrrfixsearch(struct mem_range_softc *sc,
91						    u_int64_t addr);
92static int			i686_mrsetlow(struct mem_range_softc *sc,
93					      struct mem_range_desc *mrd,
94					      int *arg);
95static int			i686_mrsetvariable(struct mem_range_softc *sc,
96						   struct mem_range_desc *mrd,
97						   int *arg);
98
99/* i686 MTRR type to memory range type conversion */
100static int i686_mtrrtomrt[] = {
101    MDF_UNCACHEABLE,
102    MDF_WRITECOMBINE,
103    0,
104    0,
105    MDF_WRITETHROUGH,
106    MDF_WRITEPROTECT,
107    MDF_WRITEBACK
108};
109
110/*
111 * i686 MTRR conflict matrix for overlapping ranges
112 *
113 * Specifically, this matrix allows writeback and uncached ranges
114 * to overlap (the overlapped region is uncached).  The array index
115 * is the translated i686 code for the flags (because they map well).
116 */
117static int i686_mtrrconflict[] = {
118    MDF_WRITECOMBINE | MDF_WRITETHROUGH | MDF_WRITEPROTECT,
119    MDF_ATTRMASK,
120    0,
121    0,
122    MDF_ATTRMASK,
123    MDF_ATTRMASK,
124    MDF_WRITECOMBINE | MDF_WRITETHROUGH | MDF_WRITEPROTECT
125};
126
127/*
128 * Look for an exactly-matching range.
129 */
130static struct mem_range_desc *
131mem_range_match(struct mem_range_softc *sc, struct mem_range_desc *mrd)
132{
133    struct mem_range_desc	*cand;
134    int				i;
135
136    for (i = 0, cand = sc->mr_desc; i < sc->mr_ndesc; i++, cand++)
137	if ((cand->mr_base == mrd->mr_base) &&
138	    (cand->mr_len == mrd->mr_len))
139	    return(cand);
140    return(NULL);
141}
142
143/*
144 * Fetch the current mtrr settings from the current CPU (assumed to all
145 * be in sync in the SMP case).  Note that if we are here, we assume
146 * that MTRRs are enabled, and we may or may not have fixed MTRRs.
147 */
148static void
149i686_mrfetch(struct mem_range_softc *sc)
150{
151    struct mem_range_desc	*mrd;
152    u_int64_t			msrv;
153    int				i, j, msr;
154
155    mrd = sc->mr_desc;
156
157    /* Get fixed-range MTRRs */
158    if (sc->mr_cap & MR686_FIXMTRR) {
159	msr = MSR_MTRR64kBase;
160	for (i = 0; i < (MTRR_N64K / 8); i++, msr++) {
161	    msrv = rdmsr(msr);
162	    for (j = 0; j < 8; j++, mrd++) {
163		mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
164		    i686_mtrrtomrt[msrv & 0xff] |
165		    MDF_ACTIVE;
166		if (mrd->mr_owner[0] == 0)
167		    strcpy(mrd->mr_owner, mem_owner_bios);
168		msrv = msrv >> 8;
169	    }
170	}
171	msr = MSR_MTRR16kBase;
172	for (i = 0; i < (MTRR_N16K / 8); i++, msr++) {
173	    msrv = rdmsr(msr);
174	    for (j = 0; j < 8; j++, mrd++) {
175		mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
176		    i686_mtrrtomrt[msrv & 0xff] |
177		    MDF_ACTIVE;
178		if (mrd->mr_owner[0] == 0)
179		    strcpy(mrd->mr_owner, mem_owner_bios);
180		msrv = msrv >> 8;
181	    }
182	}
183	msr = MSR_MTRR4kBase;
184	for (i = 0; i < (MTRR_N4K / 8); i++, msr++) {
185	    msrv = rdmsr(msr);
186	    for (j = 0; j < 8; j++, mrd++) {
187		mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
188		    i686_mtrrtomrt[msrv & 0xff] |
189		    MDF_ACTIVE;
190		if (mrd->mr_owner[0] == 0)
191		    strcpy(mrd->mr_owner, mem_owner_bios);
192		msrv = msrv >> 8;
193	    }
194	}
195    }
196
197    /* Get remainder which must be variable MTRRs */
198    msr = MSR_MTRRVarBase;
199    for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) {
200	msrv = rdmsr(msr);
201	mrd->mr_flags = (mrd->mr_flags & ~MDF_ATTRMASK) |
202	    i686_mtrrtomrt[msrv & 0xff];
203	mrd->mr_base = msrv & 0x0000000ffffff000LL;
204	msrv = rdmsr(msr + 1);
205	mrd->mr_flags = (msrv & 0x800) ?
206	    (mrd->mr_flags | MDF_ACTIVE) :
207	    (mrd->mr_flags & ~MDF_ACTIVE);
208	/* Compute the range from the mask. Ick. */
209	mrd->mr_len = (~(msrv & 0x0000000ffffff000LL) & 0x0000000fffffffffLL) + 1;
210	if (!mrvalid(mrd->mr_base, mrd->mr_len))
211	    mrd->mr_flags |= MDF_BOGUS;
212	/* If unclaimed and active, must be the BIOS */
213	if ((mrd->mr_flags & MDF_ACTIVE) && (mrd->mr_owner[0] == 0))
214	    strcpy(mrd->mr_owner, mem_owner_bios);
215    }
216}
217
218/*
219 * Return the MTRR memory type matching a region's flags
220 */
221static int
222i686_mtrrtype(int flags)
223{
224    int		i;
225
226    flags &= MDF_ATTRMASK;
227
228    for (i = 0; i < (sizeof(i686_mtrrtomrt) / sizeof(i686_mtrrtomrt[0])); i++) {
229	if (i686_mtrrtomrt[i] == 0)
230	    continue;
231	if (flags == i686_mtrrtomrt[i])
232	    return(i);
233    }
234    return(-1);
235}
236
237/*
238 * Update running CPU(s) MTRRs to match the ranges in the descriptor
239 * list.
240 *
241 * XXX Must be called with interrupts enabled.
242 */
243static void
244i686_mrstore(struct mem_range_softc *sc)
245{
246#ifdef SMP
247    /*
248     * We should use all_but_self_ipi() to call other CPUs into a
249     * locking gate, then call a target function to do this work.
250     * The "proper" solution involves a generalised locking gate
251     * implementation, not ready yet.
252     */
253    smp_rendezvous(NULL, i686_mrstoreone, NULL, (void *)sc);
254#else
255    disable_intr();				/* disable interrupts */
256    i686_mrstoreone((void *)sc);
257    enable_intr();
258#endif
259}
260
261/*
262 * Update the current CPU's MTRRs with those represented in the
263 * descriptor list.  Note that we do this wholesale rather than
264 * just stuffing one entry; this is simpler (but slower, of course).
265 */
266static void
267i686_mrstoreone(void *arg)
268{
269    struct mem_range_softc 	*sc = (struct mem_range_softc *)arg;
270    struct mem_range_desc	*mrd;
271    u_int64_t			msrv;
272    int				i, j, msr;
273    u_int			cr4save;
274
275    mrd = sc->mr_desc;
276
277    cr4save = rcr4();				/* save cr4 */
278    if (cr4save & CR4_PGE)
279	load_cr4(cr4save & ~CR4_PGE);
280    load_cr0((rcr0() & ~CR0_NW) | CR0_CD);	/* disable caches (CD = 1, NW = 0) */
281    wbinvd();					/* flush caches, TLBs */
282    wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) & ~0x800);	/* disable MTRRs (E = 0) */
283
284    /* Set fixed-range MTRRs */
285    if (sc->mr_cap & MR686_FIXMTRR) {
286	msr = MSR_MTRR64kBase;
287	for (i = 0; i < (MTRR_N64K / 8); i++, msr++) {
288	    msrv = 0;
289	    for (j = 7; j >= 0; j--) {
290		msrv = msrv << 8;
291		msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff);
292	    }
293	    wrmsr(msr, msrv);
294	    mrd += 8;
295	}
296	msr = MSR_MTRR16kBase;
297	for (i = 0; i < (MTRR_N16K / 8); i++, msr++) {
298	    msrv = 0;
299	    for (j = 7; j >= 0; j--) {
300		msrv = msrv << 8;
301		msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff);
302	    }
303	    wrmsr(msr, msrv);
304	    mrd += 8;
305	}
306	msr = MSR_MTRR4kBase;
307	for (i = 0; i < (MTRR_N4K / 8); i++, msr++) {
308	    msrv = 0;
309	    for (j = 7; j >= 0; j--) {
310		msrv = msrv << 8;
311		msrv |= (i686_mtrrtype((mrd + j)->mr_flags) & 0xff);
312	    }
313	    wrmsr(msr, msrv);
314	    mrd += 8;
315	}
316    }
317
318    /* Set remainder which must be variable MTRRs */
319    msr = MSR_MTRRVarBase;
320    for (; (mrd - sc->mr_desc) < sc->mr_ndesc; msr += 2, mrd++) {
321	/* base/type register */
322	if (mrd->mr_flags & MDF_ACTIVE) {
323	    msrv = mrd->mr_base & 0x0000000ffffff000LL;
324	    msrv |= (i686_mtrrtype(mrd->mr_flags) & 0xff);
325	} else {
326	    msrv = 0;
327	}
328	wrmsr(msr, msrv);
329
330	/* mask/active register */
331	if (mrd->mr_flags & MDF_ACTIVE) {
332	    msrv = 0x800 | (~(mrd->mr_len - 1) & 0x0000000ffffff000LL);
333	} else {
334	    msrv = 0;
335	}
336	wrmsr(msr + 1, msrv);
337    }
338    wbinvd();							/* flush caches, TLBs */
339    wrmsr(MSR_MTRRdefType, rdmsr(MSR_MTRRdefType) | 0x800);	/* restore MTRR state */
340    load_cr0(rcr0() & ~(CR0_CD | CR0_NW));  			/* enable caches CD = 0 and NW = 0 */
341    load_cr4(cr4save);						/* restore cr4 */
342}
343
344/*
345 * Hunt for the fixed MTRR referencing (addr)
346 */
347static struct mem_range_desc *
348i686_mtrrfixsearch(struct mem_range_softc *sc, u_int64_t addr)
349{
350    struct mem_range_desc *mrd;
351    int			i;
352
353    for (i = 0, mrd = sc->mr_desc; i < (MTRR_N64K + MTRR_N16K + MTRR_N4K); i++, mrd++)
354	if ((addr >= mrd->mr_base) && (addr < (mrd->mr_base + mrd->mr_len)))
355	    return(mrd);
356    return(NULL);
357}
358
359/*
360 * Try to satisfy the given range request by manipulating the fixed MTRRs that
361 * cover low memory.
362 *
363 * Note that we try to be generous here; we'll bloat the range out to the
364 * next higher/lower boundary to avoid the consumer having to know too much
365 * about the mechanisms here.
366 *
367 * XXX note that this will have to be updated when we start supporting "busy" ranges.
368 */
369static int
370i686_mrsetlow(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
371{
372    struct mem_range_desc	*first_md, *last_md, *curr_md;
373
374    /* range check */
375    if (((first_md = i686_mtrrfixsearch(sc, mrd->mr_base)) == NULL) ||
376	((last_md = i686_mtrrfixsearch(sc, mrd->mr_base + mrd->mr_len - 1)) == NULL))
377	return(EINVAL);
378
379    /* set flags, clear set-by-firmware flag */
380    for (curr_md = first_md; curr_md <= last_md; curr_md++) {
381	curr_md->mr_flags = mrcopyflags(curr_md->mr_flags & ~MDF_FIRMWARE, mrd->mr_flags);
382	bcopy(mrd->mr_owner, curr_md->mr_owner, sizeof(mrd->mr_owner));
383    }
384
385    return(0);
386}
387
388
389/*
390 * Modify/add a variable MTRR to satisfy the request.
391 *
392 * XXX needs to be updated to properly support "busy" ranges.
393 */
394static int
395i686_mrsetvariable(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
396{
397    struct mem_range_desc	*curr_md, *free_md;
398    int				i;
399
400    /*
401     * Scan the currently active variable descriptors, look for
402     * one we exactly match (straight takeover) and for possible
403     * accidental overlaps.
404     * Keep track of the first empty variable descriptor in case we
405     * can't perform a takeover.
406     */
407    i = (sc->mr_cap & MR686_FIXMTRR) ? MTRR_N64K + MTRR_N16K + MTRR_N4K : 0;
408    curr_md = sc->mr_desc + i;
409    free_md = NULL;
410    for (; i < sc->mr_ndesc; i++, curr_md++) {
411	if (curr_md->mr_flags & MDF_ACTIVE) {
412	    /* exact match? */
413	    if ((curr_md->mr_base == mrd->mr_base) &&
414		(curr_md->mr_len == mrd->mr_len)) {
415		/* whoops, owned by someone */
416		if (curr_md->mr_flags & MDF_BUSY)
417		    return(EBUSY);
418		/* Ok, just hijack this entry */
419		free_md = curr_md;
420		break;
421	    }
422	    /* non-exact overlap ? */
423	    if (mroverlap(curr_md, mrd)) {
424		/* between conflicting region types? */
425		if ((i686_mtrrconflict[i686_mtrrtype(curr_md->mr_flags)] & mrd->mr_flags) ||
426		    (i686_mtrrconflict[i686_mtrrtype(mrd->mr_flags)] & curr_md->mr_flags))
427		    return(EINVAL);
428	    }
429	} else if (free_md == NULL) {
430	    free_md = curr_md;
431	}
432    }
433    /* got somewhere to put it? */
434    if (free_md == NULL)
435	return(ENOSPC);
436
437    /* Set up new descriptor */
438    free_md->mr_base = mrd->mr_base;
439    free_md->mr_len = mrd->mr_len;
440    free_md->mr_flags = mrcopyflags(MDF_ACTIVE, mrd->mr_flags);
441    bcopy(mrd->mr_owner, free_md->mr_owner, sizeof(mrd->mr_owner));
442    return(0);
443}
444
445/*
446 * Handle requests to set memory range attributes by manipulating MTRRs.
447 *
448 */
449static int
450i686_mrset(struct mem_range_softc *sc, struct mem_range_desc *mrd, int *arg)
451{
452    struct mem_range_desc	*targ;
453    int				error = 0;
454
455    switch(*arg) {
456    case MEMRANGE_SET_UPDATE:
457	/* make sure that what's being asked for is even possible at all */
458	if (!mrvalid(mrd->mr_base, mrd->mr_len) ||
459	    (i686_mtrrtype(mrd->mr_flags & MDF_ATTRMASK) == -1))
460	    return(EINVAL);
461
462#define FIXTOP	((MTRR_N64K * 0x10000) + (MTRR_N16K * 0x4000) + (MTRR_N4K * 0x1000))
463
464	/* are the "low memory" conditions applicable? */
465	if ((sc->mr_cap & MR686_FIXMTRR) &&
466	    ((mrd->mr_base + mrd->mr_len) <= FIXTOP)) {
467	    if ((error = i686_mrsetlow(sc, mrd, arg)) != 0)
468		return(error);
469	} else {
470	    /* it's time to play with variable MTRRs */
471	    if ((error = i686_mrsetvariable(sc, mrd, arg)) != 0)
472		return(error);
473	}
474	break;
475
476    case MEMRANGE_SET_REMOVE:
477	if ((targ = mem_range_match(sc, mrd)) == NULL)
478	    return(ENOENT);
479	if (targ->mr_flags & MDF_FIXACTIVE)
480	    return(EPERM);
481	if (targ->mr_flags & MDF_BUSY)
482	    return(EBUSY);
483	targ->mr_flags &= ~MDF_ACTIVE;
484	targ->mr_owner[0] = 0;
485	break;
486
487    default:
488	return(EOPNOTSUPP);
489    }
490
491    /* update the hardware */
492    i686_mrstore(sc);
493    i686_mrfetch(sc);	/* refetch to see where we're at */
494    return(0);
495}
496
497/*
498 * Work out how many ranges we support, initialise storage for them,
499 * fetch the initial settings.
500 */
501static void
502i686_mrinit(struct mem_range_softc *sc)
503{
504    struct mem_range_desc	*mrd;
505    int				nmdesc = 0;
506    int				i;
507
508    mtrrcap = rdmsr(MSR_MTRRcap);
509    mtrrdef = rdmsr(MSR_MTRRdefType);
510
511    /* For now, bail out if MTRRs are not enabled */
512    if (!(mtrrdef & 0x800)) {
513	if (bootverbose)
514	    printf("CPU supports MTRRs but not enabled\n");
515	return;
516    }
517    nmdesc = mtrrcap & 0xff;
518    printf("Pentium Pro MTRR support enabled\n");
519
520    /* If fixed MTRRs supported and enabled */
521    if ((mtrrcap & 0x100) && (mtrrdef & 0x400)) {
522	sc->mr_cap = MR686_FIXMTRR;
523	nmdesc += MTRR_N64K + MTRR_N16K + MTRR_N4K;
524    }
525
526    sc->mr_desc =
527	(struct mem_range_desc *)malloc(nmdesc * sizeof(struct mem_range_desc),
528					M_MEMDESC, M_WAITOK);
529    bzero(sc->mr_desc, nmdesc * sizeof(struct mem_range_desc));
530    sc->mr_ndesc = nmdesc;
531
532    mrd = sc->mr_desc;
533
534    /* Populate the fixed MTRR entries' base/length */
535    if (sc->mr_cap & MR686_FIXMTRR) {
536	for (i = 0; i < MTRR_N64K; i++, mrd++) {
537	    mrd->mr_base = i * 0x10000;
538	    mrd->mr_len = 0x10000;
539	    mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
540	}
541	for (i = 0; i < MTRR_N16K; i++, mrd++) {
542	    mrd->mr_base = i * 0x4000 + 0x80000;
543	    mrd->mr_len = 0x4000;
544	    mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
545	}
546	for (i = 0; i < MTRR_N4K; i++, mrd++) {
547	    mrd->mr_base = i * 0x1000 + 0xc0000;
548	    mrd->mr_len = 0x1000;
549	    mrd->mr_flags = MDF_FIXBASE | MDF_FIXLEN | MDF_FIXACTIVE;
550	}
551    }
552
553    /*
554     * Get current settings, anything set now is considered to have
555     * been set by the firmware. (XXX has something already played here?)
556     */
557    i686_mrfetch(sc);
558    mrd = sc->mr_desc;
559    for (i = 0; i < sc->mr_ndesc; i++, mrd++) {
560	if (mrd->mr_flags & MDF_ACTIVE)
561	    mrd->mr_flags |= MDF_FIRMWARE;
562    }
563}
564
565/*
566 * Initialise MTRRs on an AP after the BSP has run the init code.
567 */
568static void
569i686_mrAPinit(struct mem_range_softc *sc)
570{
571    i686_mrstoreone((void *)sc);	/* set MTRRs to match BSP */
572    wrmsr(MSR_MTRRdefType, mtrrdef);	/* set MTRR behaviour to match BSP */
573}
574
575static void
576i686_mem_drvinit(void *unused)
577{
578    /* Try for i686 MTRRs */
579    if (!strcmp(cpu_vendor, "GenuineIntel") &&
580	cpu_feature & CPUID_MTRR &&
581	(cpu_id & 0xf00) == 0x600) {
582	mem_range_softc.mr_op = &i686_mrops;
583    }
584}
585
586SYSINIT(i686memdev,SI_SUB_DRIVERS,SI_ORDER_FIRST,i686_mem_drvinit,NULL)
587
588
589