• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2009 H. Peter Anvin - All Rights Reserved
5  *
6  *   Permission is hereby granted, free of charge, to any person
7  *   obtaining a copy of this software and associated documentation
8  *   files (the "Software"), to deal in the Software without
9  *   restriction, including without limitation the rights to use,
10  *   copy, modify, merge, publish, distribute, sublicense, and/or
11  *   sell copies of the Software, and to permit persons to whom
12  *   the Software is furnished to do so, subject to the following
13  *   conditions:
14  *
15  *   The above copyright notice and this permission notice shall
16  *   be included in all copies or substantial portions of the Software.
17  *
18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * ----------------------------------------------------------------------- */
28 
29 /*
30  * mem.c
31  *
32  * Obtain a memory map for a Multiboot OS
33  *
34  * This differs from the libcom32 memory map functions in that it doesn't
35  * attempt to filter out memory regions...
36  */
37 
38 #include "mboot.h"
39 #include <com32.h>
40 
41 struct e820_entry {
42     uint64_t start;
43     uint64_t len;
44     uint32_t type;
45 };
46 
47 #define RANGE_ALLOC_BLOCK	128
48 
mboot_scan_memory(struct AddrRangeDesc ** ardp,uint32_t * dosmem)49 static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t * dosmem)
50 {
51     com32sys_t ireg, oreg;
52     struct e820_entry *e820buf;
53     struct AddrRangeDesc *ard;
54     size_t ard_count, ard_space;
55     int rv = 0;
56 
57     /* Use INT 12h to get DOS memory */
58     __intcall(0x12, &__com32_zero_regs, &oreg);
59     *dosmem = oreg.eax.w[0] << 10;
60     if (*dosmem < 32 * 1024 || *dosmem > 640 * 1024) {
61 	/* INT 12h reports nonsense... now what? */
62 	uint16_t ebda_seg = *(uint16_t *) 0x40e;
63 	if (ebda_seg >= 0x8000 && ebda_seg < 0xa000)
64 	    *dosmem = ebda_seg << 4;
65 	else
66 	    *dosmem = 640 * 1024;	/* Hope for the best... */
67     }
68 
69     e820buf = lmalloc(sizeof(*e820buf));
70     if (!e820buf)
71 	return 0;
72 
73     /* Allocate initial space */
74     *ardp = ard = malloc(RANGE_ALLOC_BLOCK * sizeof *ard);
75     if (!ard)
76 	goto out;
77 
78     ard_count = 0;
79     ard_space = RANGE_ALLOC_BLOCK;
80 
81     /* First try INT 15h AX=E820h */
82     memset(&ireg, 0, sizeof ireg);
83     ireg.eax.l = 0xe820;
84     ireg.edx.l = 0x534d4150;
85     /* ireg.ebx.l    = 0; */
86     ireg.ecx.l = sizeof(*e820buf);
87     ireg.es = SEG(e820buf);
88     ireg.edi.w[0] = OFFS(e820buf);
89     memset(e820buf, 0, sizeof *e820buf);
90 
91     do {
92 	__intcall(0x15, &ireg, &oreg);
93 
94 	if ((oreg.eflags.l & EFLAGS_CF) ||
95 	    (oreg.eax.l != 0x534d4150) || (oreg.ecx.l < 20))
96 	    break;
97 
98 	if (ard_count >= ard_space) {
99 	    ard_space += RANGE_ALLOC_BLOCK;
100 	    *ardp = ard = realloc(ard, ard_space * sizeof *ard);
101 	    if (!ard) {
102 		rv = ard_count;
103 		goto out;
104 	    }
105 	}
106 
107 	ard[ard_count].size = 20;
108 	ard[ard_count].BaseAddr = e820buf->start;
109 	ard[ard_count].Length = e820buf->len;
110 	ard[ard_count].Type = e820buf->type;
111 	ard_count++;
112 
113 	ireg.ebx.l = oreg.ebx.l;
114     } while (oreg.ebx.l);
115 
116     if (ard_count) {
117 	rv = ard_count;
118 	goto out;
119     };
120 
121     ard[0].size = 20;
122     ard[0].BaseAddr = 0;
123     ard[0].Length = *dosmem << 10;
124     ard[0].Type = 1;
125 
126     /* Next try INT 15h AX=E801h */
127     memset(&ireg, 0, sizeof ireg);
128     ireg.eax.w[0] = 0xe801;
129     __intcall(0x15, &ireg, &oreg);
130 
131     if (!(oreg.eflags.l & EFLAGS_CF) && oreg.ecx.w[0]) {
132 	ard[1].size = 20;
133 	ard[1].BaseAddr = 1 << 20;
134 	ard[1].Length = oreg.ecx.w[0] << 10;
135 	ard[1].Type = 1;
136 
137 	if (oreg.edx.w[0]) {
138 	    ard[2].size = 20;
139 	    ard[2].BaseAddr = 16 << 20;
140 	    ard[2].Length = oreg.edx.w[0] << 16;
141 	    ard[2].Type = 1;
142 	    rv = 3;
143 	} else {
144 	    rv = 2;
145 	}
146 
147 	goto out;
148     }
149 
150     /* Finally try INT 15h AH=88h */
151     memset(&ireg, 0, sizeof ireg);
152     ireg.eax.w[0] = 0x8800;
153     __intcall(0x15, &ireg, &oreg);
154     if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) {
155 	ard[1].size = 20;
156 	ard[1].BaseAddr = 1 << 20;
157 	ard[1].Length = oreg.ecx.w[0] << 10;
158 	ard[1].Type = 1;
159 	rv = 2;
160 	goto out;
161     }
162 
163     rv = 1;			/* ... problematic ... */
164 out:
165     lfree(e820buf);
166     return rv;
167 }
168 
mboot_make_memmap(void)169 void mboot_make_memmap(void)
170 {
171     int i, nmap;
172     struct AddrRangeDesc *ard;
173     uint32_t lowmem, highmem;
174     uint32_t highrsvd;
175 
176     /* Always report DOS memory as "lowmem", this may be overly conservative
177        (e.g. if we're dropping PXE), but it should be *safe*... */
178 
179     nmap = mboot_scan_memory(&ard, &lowmem);
180 
181     highmem = 0x100000;
182     highrsvd = 0xfff00000;
183 
184 again:
185     for (i = 0; i < nmap; i++) {
186 	uint64_t start, end;
187 
188 	start = ard[i].BaseAddr;
189 	end = start + ard[i].Length;
190 
191 	if (end < start)
192 	    end = ~0ULL;
193 
194 	if (start & 0xffffffff00000000ULL)
195 	    continue;		/* Not interested in 64-bit memory */
196 
197 	if (start < highmem)
198 	    start = highmem;
199 
200 	if (end <= start)
201 	    continue;
202 
203 	if (ard[i].Type == 1 && start == highmem) {
204 	    highmem = end;
205 	    goto again;
206 	} else if (ard[i].Type != 1 && start < highrsvd)
207 	    highrsvd = start;
208     }
209 
210     if (highmem > highrsvd)
211 	highmem = highrsvd;
212 
213     mbinfo.mem_lower = lowmem >> 10;
214     mbinfo.mem_upper = (highmem - 0x100000) >> 10;
215     mbinfo.flags |= MB_INFO_MEMORY;
216 
217     /* The spec says this address should be +4, but Grub disagrees */
218     mbinfo.mmap_addr = map_data(ard, nmap * sizeof *ard, 4, false);
219     if (mbinfo.mmap_addr) {
220 	mbinfo.mmap_length = nmap * sizeof *ard;
221 	mbinfo.flags |= MB_INFO_MEM_MAP;
222     }
223 }
224