source: bootcd/isolinux/syslinux-6.03/com32/gpllib/memory.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 14.2 KB
Line 
1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2009 Pierre-Alexandre Meyer
4 *
5 *   Some parts borrowed from meminfo.c32:
6 *
7 *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
8 *   Copyright 2009 Intel Corporation; author: H. Peter Anvin
9 *
10 *   Some parts borrowed from Linux:
11 *
12 *   Copyright (C) 1991, 1992 Linus Torvalds
13 *   Copyright 2007 rPath, Inc. - All Rights Reserved
14 *   Copyright 2009 Intel Corporation; author H. Peter Anvin
15 *
16 *   Interrupt list from Ralf Brown (http://www.cs.cmu.edu/~ralf/files.html)
17 *
18 *   This file is part of Syslinux, and is made available under
19 *   the terms of the GNU General Public License version 2.
20 *
21 * ----------------------------------------------------------------------- */
22
23#include <stdint.h>
24#include <com32.h>
25#include <string.h>
26#include <memory.h>
27
28const char *const e820_types[] = {
29    "usable",
30    "reserved",
31    "ACPI reclaim",
32    "ACPI NVS",
33    "unusable",
34};
35
36struct e820_ext_entry {
37    struct e820entry std;
38    uint32_t ext_flags;
39} __attribute__ ((packed));
40
41#define SMAP    0x534d4150      /* ASCII "SMAP" */
42
43void get_type(int type, char *type_ptr, int type_ptr_sz)
44{
45    unsigned int real_type = type - 1;
46    if (real_type < sizeof(e820_types) / sizeof(e820_types[0]))
47        strlcpy(type_ptr, e820_types[real_type], type_ptr_sz);
48}
49
50/**
51 *INT 15 - newer BIOSes - GET SYSTEM MEMORY MAP
52 *      AX = E820h
53 *      EAX = 0000E820h
54 *      EDX = 534D4150h ('SMAP')
55 *      EBX = continuation value or 00000000h to start at beginning of map
56 *      ECX = size of buffer for result, in bytes (should be >= 20 bytes)
57 *      ES:DI -> buffer for result (see #00581)
58 *
59 * Return: CF clear if successful
60 *          EAX = 534D4150h ('SMAP')
61 *          ES:DI buffer filled
62 *          EBX = next offset from which to copy or 00000000h if all done
63 *          ECX = actual length returned in bytes
64 *      CF set on error
65 *          AH = error code (86h) (see #00496 at INT 15/AH=80h)
66 *
67 * Notes: originally introduced with the Phoenix BIOS v4.0, this function is
68 *        now supported by most newer BIOSes, since various versions of Windows
69 *        call it to find out about the system memory
70 *      a maximum of 20 bytes will be transferred at one time, even if ECX is
71 *        higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the
72 *        value of ECX on entry, and always copy 20 bytes
73 *      some BIOSes expect the high word of EAX to be clear on entry, i.e.
74 *        EAX=0000E820h
75 *      if this function is not supported, an application should fall back
76 *        to AX=E802h, AX=E801h, and then AH=88h
77 *      the BIOS is permitted to return a nonzero continuation value in EBX
78 *        and indicate that the end of the list has already been reached by
79 *        returning with CF set on the next iteration
80 *      this function will return base memory and ISA/PCI memory contiguous
81 *        with base memory as normal memory ranges; it will indicate
82 *        chipset-defined address holes which are not in use and motherboard
83 *        memory-mapped devices, and all occurrences of the system BIOS as
84 *        reserved; standard PC address ranges will not be reported
85 **/
86void detect_memory_e820(struct e820entry *desc, int size_map, int *size_found)
87{
88    int count = 0;
89    static struct e820_ext_entry buf;   /* static so it is zeroed */
90    void *bounce;
91
92    com32sys_t ireg, oreg;
93    memset(&ireg, 0, sizeof ireg);
94
95    bounce = lmalloc(sizeof buf);
96    if (!bounce)
97        goto out;
98
99    ireg.eax.w[0] = 0xe820;
100    ireg.edx.l = SMAP;
101    ireg.ecx.l = sizeof(struct e820_ext_entry);
102    ireg.edi.w[0] = OFFS(bounce);
103    ireg.es = SEG(bounce);
104
105    /*
106     * Set this here so that if the BIOS doesn't change this field
107     * but still doesn't change %ecx, we're still okay...
108     */
109    memset(&buf, 0, sizeof buf);
110    buf.ext_flags = 1;
111
112    do {
113        memcpy(bounce, &buf, sizeof buf);
114
115        /* Important: %edx and %esi are clobbered by some BIOSes,
116           so they must be either used for the error output
117           or explicitly marked clobbered.  Given that, assume there
118           is something out there clobbering %ebp and %edi, too. */
119        __intcall(0x15, &ireg, &oreg);
120
121        /* Some BIOSes stop returning SMAP in the middle of
122           the search loop.  We don't know exactly how the BIOS
123           screwed up the map at that point, we might have a
124           partial map, the full map, or complete garbage, so
125           just return failure. */
126        if (oreg.eax.l != SMAP) {
127            count = 0;
128            break;
129        }
130
131        if (oreg.eflags.l & EFLAGS_CF || oreg.ecx.l < 20)
132            break;
133
134        memcpy(&buf, bounce, sizeof buf);
135
136        /*
137         * ACPI 3.0 added the extended flags support.  If bit 0
138         * in the extended flags is zero, we're supposed to simply
139         * ignore the entry -- a backwards incompatible change!
140         */
141        if (oreg.ecx.l > 20 && !(buf.ext_flags & 1))
142            continue;
143
144        memcpy(&desc[count], &buf, sizeof buf);
145        count++;
146
147        /* Set continuation value */
148        ireg.ebx.l = oreg.ebx.l;
149    } while (ireg.ebx.l && count < size_map);
150
151out:
152    lfree(bounce);
153    *size_found = count;
154}
155
156/**
157 * detect_memory_e801
158 *
159 *INT 15 - Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
160 *      AX = E801h
161 *
162 * Return: CF clear if successful
163 *          AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
164 *          BX = extended memory above 16M, in 64K blocks
165 *          CX = configured memory 1M to 16M, in K
166 *          DX = configured memory above 16M, in 64K blocks
167 *      CF set on error
168 *
169 * Notes: supported by the A03 level (6/14/94) and later XPS P90 BIOSes, as well
170 *      as the Compaq Contura, 3/8/93 DESKPRO/i, and 7/26/93 LTE Lite 386 ROM
171 *      BIOS
172 *      supported by AMI BIOSes dated 8/23/94 or later
173 *      on some systems, the BIOS returns AX=BX=0000h; in this case, use CX
174 *        and DX instead of AX and BX
175 *      this interface is used by Windows NT 3.1, OS/2 v2.11/2.20, and is
176 *        used as a fall-back by newer versions if AX=E820h is not supported
177 *      this function is not used by MS-DOS 6.0 HIMEM.SYS when an EISA machine
178 *        (for example with parameter /EISA) (see also MEM F000h:FFD9h), or no
179 *        Compaq machine was detected, or parameter /NOABOVE16 was given.
180 **/
181int detect_memory_e801(int *mem_size_below_16, int *mem_size_above_16)
182{
183    com32sys_t ireg, oreg;
184    memset(&ireg, 0, sizeof ireg);
185
186    ireg.eax.w[0] = 0xe801;
187
188    __intcall(0x15, &ireg, &oreg);
189
190    if (oreg.eflags.l & EFLAGS_CF)
191        return -1;
192
193    if (oreg.eax.w[0] > 0x3c00)
194        return -1;              /* Bogus! */
195
196    /* Linux seems to use ecx and edx by default if they are defined */
197    if (oreg.eax.w[0] || oreg.eax.w[0]) {
198        oreg.eax.w[0] = oreg.ecx.w[0];
199        oreg.ebx.w[0] = oreg.edx.w[0];
200    }
201
202    *mem_size_below_16 = oreg.eax.w[0]; /* 1K blocks */
203    *mem_size_above_16 = oreg.ebx.w[0]; /* 64K blocks */
204
205    return 0;
206}
207
208int detect_memory_88(int *mem_size)
209{
210    com32sys_t ireg, oreg;
211    memset(&ireg, 0, sizeof ireg);
212
213    ireg.eax.w[0] = 0x8800;
214
215    __intcall(0x15, &ireg, &oreg);
216
217    if (oreg.eflags.l & EFLAGS_CF)
218        return -1;
219
220    *mem_size = oreg.eax.w[0];
221    return 0;
222}
223
224/*
225 * Sanitize the BIOS e820 map.
226 *
227 * This code come from the memtest86 project. It have been adjusted to match
228 * the syslinux environement.
229 * Some e820 responses include overlapping entries.  The following
230 * replaces the original e820 map with a new one, removing overlaps.
231 *
232 * The following stuff could be merge once the addr_t will be set to 64bits.
233 * syslinux_scan_memory can be used for that purpose
234 */
235int sanitize_e820_map(struct e820entry *orig_map, struct e820entry *new_bios,
236                      short old_nr)
237{
238    struct change_member {
239        struct e820entry *pbios;        /* pointer to original bios entry */
240        unsigned long long addr;        /* address for this change point */
241    };
242    struct change_member change_point_list[2 * E820MAX];
243    struct change_member *change_point[2 * E820MAX];
244    struct e820entry *overlap_list[E820MAX];
245    struct e820entry biosmap[E820MAX];
246    struct change_member *change_tmp;
247    unsigned long current_type, last_type;
248    unsigned long long last_addr;
249    int chgidx, still_changing;
250    int overlap_entries;
251    int new_bios_entry;
252    int i;
253
254    /*
255       Visually we're performing the following (1,2,3,4 = memory types)...
256       Sample memory map (w/overlaps):
257       ____22__________________
258       ______________________4_
259       ____1111________________
260       _44_____________________
261       11111111________________
262       ____________________33__
263       ___________44___________
264       __________33333_________
265       ______________22________
266       ___________________2222_
267       _________111111111______
268       _____________________11_
269       _________________4______
270
271       Sanitized equivalent (no overlap):
272       1_______________________
273       _44_____________________
274       ___1____________________
275       ____22__________________
276       ______11________________
277       _________1______________
278       __________3_____________
279       ___________44___________
280       _____________33_________
281       _______________2________
282       ________________1_______
283       _________________4______
284       ___________________2____
285       ____________________33__
286       ______________________4_
287     */
288    /* First make a copy of the map */
289    for (i = 0; i < old_nr; i++) {
290        biosmap[i].addr = orig_map[i].addr;
291        biosmap[i].size = orig_map[i].size;
292        biosmap[i].type = orig_map[i].type;
293    }
294
295    /* bail out if we find any unreasonable addresses in bios map */
296    for (i = 0; i < old_nr; i++) {
297        if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
298            return 0;
299    }
300
301    /* create pointers for initial change-point information (for sorting) */
302    for (i = 0; i < 2 * old_nr; i++)
303        change_point[i] = &change_point_list[i];
304
305    /* record all known change-points (starting and ending addresses) */
306    chgidx = 0;
307    for (i = 0; i < old_nr; i++) {
308        change_point[chgidx]->addr = biosmap[i].addr;
309        change_point[chgidx++]->pbios = &biosmap[i];
310        change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
311        change_point[chgidx++]->pbios = &biosmap[i];
312    }
313
314    /* sort change-point list by memory addresses (low -> high) */
315    still_changing = 1;
316    while (still_changing) {
317        still_changing = 0;
318        for (i = 1; i < 2 * old_nr; i++) {
319            /* if <current_addr> > <last_addr>, swap */
320            /* or, if current=<start_addr> & last=<end_addr>, swap */
321            if ((change_point[i]->addr < change_point[i - 1]->addr) ||
322                ((change_point[i]->addr == change_point[i - 1]->addr) &&
323                 (change_point[i]->addr == change_point[i]->pbios->addr) &&
324                 (change_point[i - 1]->addr !=
325                  change_point[i - 1]->pbios->addr))
326                ) {
327                change_tmp = change_point[i];
328                change_point[i] = change_point[i - 1];
329                change_point[i - 1] = change_tmp;
330                still_changing = 1;
331            }
332        }
333    }
334
335    /* create a new bios memory map, removing overlaps */
336    overlap_entries = 0;        /* number of entries in the overlap table */
337    new_bios_entry = 0;         /* index for creating new bios map entries */
338    last_type = 0;              /* start with undefined memory type */
339    last_addr = 0;              /* start with 0 as last starting address */
340    /* loop through change-points, determining affect on the new bios map */
341    for (chgidx = 0; chgidx < 2 * old_nr; chgidx++) {
342        /* keep track of all overlapping bios entries */
343        if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr) {
344            /* add map entry to overlap list (> 1 entry implies an overlap) */
345            overlap_list[overlap_entries++] = change_point[chgidx]->pbios;
346        } else {
347            /* remove entry from list (order independent, so swap with last) */
348            for (i = 0; i < overlap_entries; i++) {
349                if (overlap_list[i] == change_point[chgidx]->pbios)
350                    overlap_list[i] = overlap_list[overlap_entries - 1];
351            }
352            overlap_entries--;
353        }
354        /* if there are overlapping entries, decide which "type" to use */
355        /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */
356        current_type = 0;
357        for (i = 0; i < overlap_entries; i++)
358            if (overlap_list[i]->type > current_type)
359                current_type = overlap_list[i]->type;
360        /* continue building up new bios map based on this information */
361        if (current_type != last_type) {
362            if (last_type != 0) {
363                new_bios[new_bios_entry].size =
364                    change_point[chgidx]->addr - last_addr;
365                /* move forward only if the new size was non-zero */
366                if (new_bios[new_bios_entry].size != 0)
367                    if (++new_bios_entry >= E820MAX)
368                        break;  /* no more space left for new bios entries */
369            }
370            if (current_type != 0) {
371                new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
372                new_bios[new_bios_entry].type = current_type;
373                last_addr = change_point[chgidx]->addr;
374            }
375            last_type = current_type;
376        }
377    }
378    return (new_bios_entry);
379}
380
381/* The following stuff could be merge once the addr_t will be set to 64bits.
382 * syslinux_scan_memory can be used for that purpose */
383unsigned long detect_memsize(void)
384{
385    unsigned long memory_size = 0;
386
387    /* Try to detect memory via e820 */
388    struct e820entry map[E820MAX];
389    int count = 0;
390    detect_memory_e820(map, E820MAX, &count);
391    memory_size = memsize_e820(map, count);
392    if (memory_size > 0)
393        return memory_size;
394
395    /*e820 failed, let's try e801 */
396    int mem_low, mem_high = 0;
397    if (!detect_memory_e801(&mem_low, &mem_high))
398        return mem_low + (mem_high << 6);
399
400    /*e801 failed, let's try e88 */
401    int mem_size = 0;
402    if (!detect_memory_88(&mem_size))
403        return mem_size;
404
405    /* We were enable to detect any kind of memory */
406    return 0;
407}
408
409/* The following stuff could be merge once the addr_t will be set to 64bits.
410 * syslinux_scan_memory can be used for that purpose */
411unsigned long memsize_e820(struct e820entry *e820, int e820_nr)
412{
413    int i, n, nr;
414    unsigned long memory_size = 0;
415    struct e820entry nm[E820MAX];
416
417    /* Clean up, adjust and copy the BIOS-supplied E820-map. */
418    nr = sanitize_e820_map(e820, nm, e820_nr);
419
420    /* If there is not a good 820 map returning 0 to indicate
421       that we don't have any idea of the amount of ram we have */
422    if (nr < 1 || nr > E820MAX) {
423        return 0;
424    }
425
426    /* Build the memory map for testing */
427    n = 0;
428    for (i = 0; i < nr; i++) {
429        if (nm[i].type == E820_RAM || nm[i].type == E820_ACPI) {
430            unsigned long long start;
431            unsigned long long end;
432            start = nm[i].addr;
433            end = start + nm[i].size;
434
435            /* Don't ever use memory between 640 and 1024k */
436            if (start > RES_START && start < RES_END) {
437                if (end < RES_END) {
438                    continue;
439                }
440                start = RES_END;
441            }
442            if (end > RES_START && end < RES_END) {
443                end = RES_START;
444            }
445            memory_size += (end >> 12) - ((start + 4095) >> 12);
446            n++;
447        } else if (nm[i].type == E820_NVS) {
448            memory_size += nm[i].size >> 12;
449        }
450    }
451    return memory_size * 4;
452}
Note: See TracBrowser for help on using the repository browser.