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