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
checkA20(void)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 *
bios_E820(bios_memmap_t * mp)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 *
bios_8800(bios_memmap_t * mp)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 *
bios_int12(bios_memmap_t * mp)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
addrprobe(u_int kloc)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 *
badprobe(bios_memmap_t * mp)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 void
memprobe(void)293 memprobe(void)
294 {
295 bios_memmap_t *pm = bios_memmap, *im;
296
297 #ifdef DEBUG
298 printf(" mem(");
299 #else
300 printf(" mem[");
301 #endif
302
303 if ((pm = bios_E820(bios_memmap)) == NULL) {
304 im = bios_int12(bios_memmap);
305 #if 0
306 pm = bios_E801(im);
307 if (pm == NULL)
308 #endif
309 pm = bios_8800(im);
310 if (pm == NULL)
311 pm = badprobe(im);
312 if (pm == NULL) {
313 printf(" No Extended memory detected.");
314 pm = im;
315 }
316 }
317 pm->type = BIOS_MAP_END;
318
319 /* XXX - gotta peephole optimize the list */
320
321 /* Remove APM needed RAM */
322 apmfixmem();
323
324 #ifdef DEBUG
325 printf(")[");
326 #endif
327
328 /* XXX - Compatibility, remove later (smpprobe() relies on it) */
329 extmem = cnvmem = 0;
330 for (im = bios_memmap; im->type != BIOS_MAP_END; im++) {
331 /* Count only "good" memory chunks 12K and up in size */
332 if ((im->type == BIOS_MAP_FREE) && (im->size >= 12*1024)) {
333 if (im->size > 1024 * 1024)
334 printf("%uM ", (u_int)(im->size /
335 (1024 * 1024)));
336 else
337 printf("%uK ", (u_int)im->size / 1024);
338
339 /*
340 * Compute compatibility values:
341 * cnvmem -- is the upper boundary of conventional
342 * memory (below IOM_BEGIN (=640k))
343 * extmem -- is the size of the contignous extended
344 * memory segment starting at 1M
345 *
346 * We ignore "good" memory in the 640K-1M hole.
347 * We drop "machine {cnvmem,extmem}" commands.
348 */
349 if (im->addr < IOM_BEGIN)
350 cnvmem = max(cnvmem,
351 im->addr + im->size) / 1024;
352 if (im->addr >= IOM_END)
353 extmem += im->size / 1024;
354 }
355 }
356
357 /* Check if gate A20 is on */
358 printf("a20=o%s] ", checkA20()? "n" : "ff!");
359 }
360
361 void
dump_biosmem(bios_memmap_t * tm)362 dump_biosmem(bios_memmap_t *tm)
363 {
364 register bios_memmap_t *p;
365 register u_int total = 0;
366
367 if (tm == NULL)
368 tm = bios_memmap;
369
370 for (p = tm; p->type != BIOS_MAP_END; p++) {
371 printf("Region %ld: type %u at 0x%llx for %uKB\n",
372 (long)(p - tm), p->type, p->addr,
373 (u_int)(p->size / 1024));
374
375 if (p->type == BIOS_MAP_FREE)
376 total += p->size / 1024;
377 }
378
379 printf("Low ram: %dKB High ram: %dKB\n", cnvmem, extmem);
380 printf("Total free memory: %uKB\n", total);
381 }
382
383 int
mem_delete(long long sa,long long ea)384 mem_delete(long long sa, long long ea)
385 {
386 register bios_memmap_t *p;
387
388 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
389 if (p->type == BIOS_MAP_FREE) {
390 register int64_t sp = p->addr, ep = p->addr + p->size;
391
392 /* can we eat it as a whole? */
393 if ((sa - sp) <= NBPG && (ep - ea) <= NBPG) {
394 bcopy(p + 1, p, (char *)bios_memmap +
395 sizeof(bios_memmap) - (char *)p);
396 break;
397 /* eat head or legs */
398 } else if (sa <= sp && sp < ea) {
399 p->addr = ea;
400 p->size = ep - ea;
401 break;
402 } else if (sa < ep && ep <= ea) {
403 p->size = sa - sp;
404 break;
405 } else if (sp < sa && ea < ep) {
406 /* bite in half */
407 bcopy(p, p + 1, (char *)bios_memmap +
408 sizeof(bios_memmap) - (char *)p -
409 sizeof(bios_memmap[0]));
410 p[1].addr = ea;
411 p[1].size = ep - ea;
412 p->size = sa - sp;
413 break;
414 }
415 }
416 }
417 return 0;
418 }
419
420 int
mem_add(long long sa,long long ea)421 mem_add(long long sa, long long ea)
422 {
423 register bios_memmap_t *p;
424
425 for (p = bios_memmap; p->type != BIOS_MAP_END; p++) {
426 if (p->type == BIOS_MAP_FREE) {
427 register int64_t sp = p->addr, ep = p->addr + p->size;
428
429 /* is it already there? */
430 if (sp <= sa && ea <= ep) {
431 break;
432 /* join head or legs */
433 } else if (sa < sp && sp <= ea) {
434 p->addr = sa;
435 p->size = ep - sa;
436 break;
437 } else if (sa <= ep && ep < ea) {
438 p->size = ea - sp;
439 break;
440 } else if (ea < sp) {
441 /* insert before */
442 bcopy(p, p + 1, (char *)bios_memmap +
443 sizeof(bios_memmap) - (char *)(p - 1));
444 p->addr = sa;
445 p->size = ea - sa;
446 break;
447 }
448 }
449 }
450
451 /* meaning add new item at the end of the list */
452 if (p->type == BIOS_MAP_END) {
453 p[1] = p[0];
454 p->type = BIOS_MAP_FREE;
455 p->addr = sa;
456 p->size = ea - sa;
457 }
458
459 return 0;
460 }
461
462 void
mem_pass(void)463 mem_pass(void)
464 {
465 bios_memmap_t *p;
466
467 for (p = bios_memmap; p->type != BIOS_MAP_END; p++)
468 ;
469 addbootarg(BOOTARG_MEMMAP, (p - bios_memmap + 1) * sizeof *bios_memmap,
470 bios_memmap);
471 }
472