root/arch/i386/stand/libsa/memprobe.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. checkA20
  2. bios_E820
  3. bios_E801
  4. bios_8800
  5. bios_int12
  6. addrprobe
  7. badprobe
  8. memprobe
  9. dump_biosmem
  10. mem_delete
  11. mem_add
  12. mem_pass

    1 /*      $OpenBSD: memprobe.c,v 1.45 2006/09/18 21:14:15 mpf Exp $       */
    2 
    3 /*
    4  * Copyright (c) 1997-1999 Michael Shalayeff
    5  * Copyright (c) 1997-1999 Tobias Weingartner
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  */
   30 
   31 #include <sys/param.h>
   32 #include <machine/biosvar.h>
   33 #include <dev/isa/isareg.h>
   34 #include <stand/boot/bootarg.h>
   35 #include "libsa.h"
   36 
   37 u_int cnvmem, extmem;           /* XXX - compatibility */
   38 
   39 
   40 /* Check gateA20
   41  *
   42  * A sanity check.
   43  */
   44 static __inline int
   45 checkA20(void)
   46 {
   47         register char *p = (char *)0x100000;
   48         register char *q = (char *)0x000000;
   49         int st;
   50 
   51         /* Simple check */
   52         if (*p != *q)
   53                 return 1;
   54 
   55         /* Complex check */
   56         *p = ~(*p);
   57         st = (*p != *q);
   58         *p = ~(*p);
   59 
   60         return st;
   61 }
   62 
   63 /* BIOS int 15, AX=E820
   64  *
   65  * This is the "preferred" method.
   66  */
   67 static __inline bios_memmap_t *
   68 bios_E820(bios_memmap_t *mp)
   69 {
   70         int rc, off = 0, sig, gotcha = 0;
   71 
   72         do {
   73                 BIOS_regs.biosr_es = ((u_int)(mp) >> 4);
   74 
   75                 __asm __volatile(DOINT(0x15) "; setc %b1"
   76                     : "=a" (sig), "=d" (rc), "=b" (off)
   77                     : "0" (0xE820), "1" (0x534d4150), "b" (off),
   78                       "c" (sizeof(*mp)), "D" (((u_int)mp) & 0xF)
   79                     : "cc", "memory");
   80                 off = BIOS_regs.biosr_bx;
   81 
   82                 if (rc & 0xff || sig != 0x534d4150)
   83                         break;
   84                 gotcha++;
   85                 if (!mp->type)
   86                         mp->type = BIOS_MAP_RES;
   87                 mp++;
   88         } while (off);
   89 
   90         if (!gotcha)
   91                 return NULL;
   92 #ifdef DEBUG
   93         printf("0x15[E820] ");
   94 #endif
   95         return mp;
   96 }
   97 
   98 #if 0
   99 /* BIOS int 15, AX=E801
  100  *
  101  * Only used if int 15, AX=E820 does not work.
  102  * This should work for more than 64MB on most
  103  * modern machines.  However, there is always
  104  * an exception, the older IBM machine do not
  105  * like this call.
  106  */
  107 static __inline bios_memmap_t *
  108 bios_E801(bios_memmap_t *mp)
  109 {
  110         int rc, m1, m2, m3, m4;
  111         u_int8_t *info;
  112 
  113         /* Test for possibility of 0xE801 */
  114         info =  getSYSCONFaddr();
  115         if (!info)
  116                 return NULL;
  117         /* XXX - Should test model/submodel/rev here */
  118         printf("model(%d,%d,%d)", info[2], info[3], info[4]);
  119 
  120         /* Check for 94 or later bios */
  121         info = (void *)0xFFFFB;
  122         if (info[0] == '9' && info[1] <= '3')
  123                 return NULL;
  124 
  125         /* We might have this call */
  126         __asm __volatile(DOINT(0x15) "; mov %%ax, %%si; setc %b0"
  127             : "=a" (rc), "=S" (m1), "=b" (m2), "=c" (m3), "=d" (m4)
  128             : "0" (0xE801));
  129 
  130         /* Test for failure */
  131         if (rc & 0xff)
  132                 return NULL;
  133 
  134         /* Fixup for screwed up machines */
  135         if (m1 == 0) {
  136                 m1 = m3;
  137                 m2 = m4;
  138         }
  139 #ifdef DEBUG
  140         printf("0x15[E801] ");
  141 #endif
  142         /* Fill out BIOS map */
  143         mp->addr = (1024 * 1024);       /* 1MB */
  144         mp->size = (m1 & 0xffff) * 1024;
  145         mp->type = BIOS_MAP_FREE;
  146 
  147         mp++;
  148         mp->addr = (1024 * 1024) * 16;  /* 16MB */
  149         mp->size = (m2 & 0xffff) * 64L * 1024;
  150         mp->type = BIOS_MAP_FREE;
  151 
  152         return ++mp;
  153 }
  154 #endif
  155 
  156 /* BIOS int 15, AX=8800
  157  *
  158  * Only used if int 15, AX=E801 does not work.
  159  * Machines with this are restricted to 64MB.
  160  */
  161 static __inline bios_memmap_t *
  162 bios_8800(bios_memmap_t *mp)
  163 {
  164         int rc, mem;
  165 
  166         __asm __volatile(DOINT(0x15) "; setc %b0"
  167             : "=c" (rc), "=a" (mem) : "a" (0x8800));
  168 
  169         if (rc & 0xff)
  170                 return NULL;
  171 #ifdef DEBUG
  172         printf("0x15[8800] ");
  173 #endif
  174         /* Fill out a BIOS_MAP */
  175         mp->addr = 1024 * 1024;         /* 1MB */
  176         mp->size = (mem & 0xffff) * 1024;
  177         mp->type = BIOS_MAP_FREE;
  178 
  179         return ++mp;
  180 }
  181 
  182 /* BIOS int 0x12 Get Conventional Memory
  183  *
  184  * Only used if int 15, AX=E820 does not work.
  185  */
  186 static __inline bios_memmap_t *
  187 bios_int12(bios_memmap_t *mp)
  188 {
  189         int mem;
  190 #ifdef DEBUG
  191         printf("0x12 ");
  192 #endif
  193         __asm __volatile(DOINT(0x12) : "=a" (mem) :: "%ecx", "%edx", "cc");
  194 
  195         /* Fill out a bios_memmap_t */
  196         mp->addr = 0;
  197         mp->size = (mem & 0xffff) * 1024;
  198         mp->type = BIOS_MAP_FREE;
  199 
  200         return ++mp;
  201 }
  202 
  203 
  204 /* addrprobe(kloc): Probe memory at address kloc * 1024.
  205  *
  206  * This is a hack, but it seems to work ok.  Maybe this is
  207  * the *real* way that you are supposed to do probing???
  208  *
  209  * Modify the original a bit.  We write everything first, and
  210  * then test for the values.  This should croak on machines that
  211  * return values just written on non-existent memory...
  212  *
  213  * BTW: These machines are pretty broken IMHO.
  214  *
  215  * XXX - Does not detect aliased memory.
  216  */
  217 const u_int addrprobe_pat[] = {
  218         0x00000000, 0xFFFFFFFF,
  219         0x01010101, 0x10101010,
  220         0x55555555, 0xCCCCCCCC
  221 };
  222 static int
  223 addrprobe(u_int kloc)
  224 {
  225         __volatile u_int *loc;
  226         register u_int i, ret = 0;
  227         u_int save[NENTS(addrprobe_pat)];
  228 
  229         /* Get location */
  230         loc = (int *)(kloc * 1024);
  231 
  232         save[0] = *loc;
  233         /* Probe address */
  234         for (i = 0; i < NENTS(addrprobe_pat); i++) {
  235                 *loc = addrprobe_pat[i];
  236                 if (*loc != addrprobe_pat[i])
  237                         ret++;
  238         }
  239         *loc = save[0];
  240 
  241         if (!ret) {
  242                 /* Write address */
  243                 for (i = 0; i < NENTS(addrprobe_pat); i++) {
  244                         save[i] = loc[i];
  245                         loc[i] = addrprobe_pat[i];
  246                 }
  247 
  248                 /* Read address */
  249                 for (i = 0; i < NENTS(addrprobe_pat); i++) {
  250                         if (loc[i] != addrprobe_pat[i])
  251                                 ret++;
  252                         loc[i] = save[i];
  253                 }
  254         }
  255 
  256         return ret;
  257 }
  258 
  259 /* Probe for all extended memory.
  260  *
  261  * This is only used as a last resort.  If we resort to this
  262  * routine, we are getting pretty desperate.  Hopefully nobody
  263  * has to rely on this after all the work above.
  264  *
  265  * XXX - Does not detect aliased memory.
  266  * XXX - Could be destructive, as it does write.
  267  */
  268 static __inline bios_memmap_t *
  269 badprobe(bios_memmap_t *mp)
  270 {
  271         u_int64_t ram;
  272 #ifdef DEBUG
  273         printf("scan ");
  274 #endif
  275         /* probe extended memory
  276          *
  277          * There is no need to do this in assembly language.  This is
  278          * much easier to debug in C anyways.
  279          */
  280         for (ram = 1024; ram < 512 * 1024; ram += 4)
  281                 if (addrprobe(ram))
  282                         break;
  283 
  284         mp->addr = 1024 * 1024;
  285         mp->size = (ram - 1024) * 1024;
  286         mp->type = BIOS_MAP_FREE;
  287 
  288         return ++mp;
  289 }
  290 
  291 bios_memmap_t bios_memmap[32];  /* This is easier */
  292 #ifndef _TEST
  293 void
  294 memprobe(void)
  295 {
  296         bios_memmap_t *pm = bios_memmap, *im;
  297 
  298 #ifdef DEBUG
  299         printf(" mem(");
  300 #else
  301         printf(" mem[");
  302 #endif
  303 
  304         if ((pm = bios_E820(bios_memmap)) == NULL) {
  305                 im = bios_int12(bios_memmap);
  306 #if 0
  307                 pm = bios_E801(im);
  308                 if (pm == NULL)
  309 #endif
  310                         pm = bios_8800(im);
  311                 if (pm == NULL)
  312                         pm = badprobe(im);
  313                 if (pm == NULL) {
  314                         printf(" No Extended memory detected.");
  315                         pm = im;
  316                 }
  317         }
  318         pm->type = BIOS_MAP_END;
  319 
  320         /* XXX - gotta peephole optimize the list */
  321 
  322         /* Remove APM needed RAM */
  323         apmfixmem();
  324 
  325 #ifdef DEBUG
  326         printf(")[");
  327 #endif
  328 
  329         /* XXX - Compatibility, remove later (smpprobe() relies on it) */
  330         extmem = cnvmem = 0;
  331         for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
  332                 /* Count only "good" memory chunks 12K and up in size */
  333                 if ((im->type == BIOS_MAP_FREE) && (im->size >= 12*1024)) {
  334                         if (im->size > 1024 * 1024)
  335                                 printf("%uM ", (u_int)(im->size /
  336                                     (1024 * 1024)));
  337                         else
  338                                 printf("%uK ", (u_int)im->size / 1024);
  339 
  340                         /*
  341                          * Compute compatibility values:
  342                          * cnvmem -- is the upper boundary of conventional
  343                          *      memory (below IOM_BEGIN (=640k))
  344                          * extmem -- is the size of the contignous extended
  345                          *      memory segment starting at 1M
  346                          *
  347                          * We ignore "good" memory in the 640K-1M hole.
  348                          * We drop "machine {cnvmem,extmem}" commands.
  349                          */
  350                         if (im->addr < IOM_BEGIN)
  351                                 cnvmem = max(cnvmem,
  352                                     im->addr + im->size) / 1024;
  353                         if (im->addr >= IOM_END)
  354                                 extmem += im->size / 1024;
  355                 }
  356         }
  357 
  358         /* Check if gate A20 is on */
  359         printf("a20=o%s] ", checkA20()? "n" : "ff!");
  360 }
  361 #endif
  362 
  363 void
  364 dump_biosmem(bios_memmap_t *tm)
  365 {
  366         register bios_memmap_t *p;
  367         register u_int total = 0;
  368 
  369         if (tm == NULL)
  370                 tm = bios_memmap;
  371 
  372         for (p = tm; p->type != BIOS_MAP_END; p++) {
  373                 printf("Region %ld: type %u at 0x%llx for %uKB\n",
  374                     (long)(p - tm), p->type, p->addr,
  375                     (u_int)(p->size / 1024));
  376 
  377                 if (p->type == BIOS_MAP_FREE)
  378                         total += p->size / 1024;
  379         }
  380 
  381         printf("Low ram: %dKB  High ram: %dKB\n", cnvmem, extmem);
  382         printf("Total free memory: %uKB\n", total);
  383 }
  384 
  385 int
  386 mem_delete(long long sa, long long ea)
  387 {
  388         register bios_memmap_t *p;
  389 
  390         for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
  391                 if (p->type == BIOS_MAP_FREE) {
  392                         register int64_t sp = p->addr, ep = p->addr + p->size;
  393 
  394                         /* can we eat it as a whole? */
  395                         if ((sa - sp) <= NBPG && (ep - ea) <= NBPG) {
  396                                 bcopy(p + 1, p, (char *)bios_memmap +
  397                                     sizeof(bios_memmap) - (char *)p);
  398                                 break;
  399                         /* eat head or legs */
  400                         } else if (sa <= sp && sp < ea) {
  401                                 p->addr = ea;
  402                                 p->size = ep - ea;
  403                                 break;
  404                         } else if (sa < ep && ep <= ea) {
  405                                 p->size = sa - sp;
  406                                 break;
  407                         } else if (sp < sa && ea < ep) {
  408                                 /* bite in half */
  409                                 bcopy(p, p + 1, (char *)bios_memmap +
  410                                     sizeof(bios_memmap) - (char *)p -
  411                                     sizeof(bios_memmap[0]));
  412                                 p[1].addr = ea;
  413                                 p[1].size = ep - ea;
  414                                 p->size = sa - sp;
  415                                 break;
  416                         }
  417                 }
  418         }
  419         return 0;
  420 }
  421 
  422 int
  423 mem_add(long long sa, long long ea)
  424 {
  425         register bios_memmap_t *p;
  426 
  427         for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
  428                 if (p->type == BIOS_MAP_FREE) {
  429                         register int64_t sp = p->addr, ep = p->addr + p->size;
  430 
  431                         /* is it already there? */
  432                         if (sp <= sa && ea <= ep) {
  433                                 break;
  434                         /* join head or legs */
  435                         } else if (sa < sp && sp <= ea) {
  436                                 p->addr = sa;
  437                                 p->size = ep - sa;
  438                                 break;
  439                         } else if (sa <= ep && ep < ea) {
  440                                 p->size = ea - sp;
  441                                 break;
  442                         } else if (ea < sp) {
  443                                 /* insert before */
  444                                 bcopy(p, p + 1, (char *)bios_memmap +
  445                                     sizeof(bios_memmap) - (char *)(p - 1));
  446                                 p->addr = sa;
  447                                 p->size = ea - sa;
  448                                 break;
  449                         }
  450                 }
  451         }
  452 
  453         /* meaning add new item at the end of the list */
  454         if (p->type == BIOS_MAP_END) {
  455                 p[1] = p[0];
  456                 p->type = BIOS_MAP_FREE;
  457                 p->addr = sa;
  458                 p->size = ea - sa;
  459         }
  460 
  461         return 0;
  462 }
  463 
  464 void
  465 mem_pass(void)
  466 {
  467         bios_memmap_t *p;
  468 
  469         for (p = bios_memmap; p->type != BIOS_MAP_END; p++)
  470                 ;
  471         addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap,
  472             bios_memmap);
  473 }

/* [<][>][^][v][top][bottom][index][help] */