* Sync up to trunk head (r64939).
[reactos.git] / boot / freeldr / freeldr / arch / i386 / pcmem.c
1 /*
2 * FreeLoader
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Note: Most of this code comes from the old file "i386mem.c", which
19 * was Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
20 */
21
22 #include <freeldr.h>
23 #include <arch/pc/x86common.h>
24
25 #define NDEBUG
26 #include <debug.h>
27
28 DBG_DEFAULT_CHANNEL(MEMORY);
29
30 #define MAX_BIOS_DESCRIPTORS 32
31
32 #define STACK_BASE_PAGE (STACKLOW / PAGE_SIZE)
33 #define FREELDR_BASE_PAGE (FREELDR_BASE / PAGE_SIZE)
34 #define DISKBUF_BASE_PAGE (DISKREADBUFFER / PAGE_SIZE)
35 #define BIOSBUF_BASE_PAGE (BIOSCALLBUFFER / PAGE_SIZE)
36
37 #define STACK_PAGE_COUNT (FREELDR_BASE_PAGE - STACK_BASE_PAGE)
38 #define FREELDR_PAGE_COUNT (DISKBUF_BASE_PAGE - FREELDR_BASE_PAGE)
39 #define DISKBUF_PAGE_COUNT (0x10)
40 #define BIOSBUF_PAGE_COUNT (1)
41
42 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
43 ULONG PcBiosMapCount;
44
45 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1] =
46 {
47 { LoaderFirmwarePermanent, 0x00, 1 }, // realmode int vectors
48 { LoaderFirmwareTemporary, 0x01, STACK_BASE_PAGE - 1 }, // freeldr stack + cmdline
49 { LoaderOsloaderStack, STACK_BASE_PAGE, FREELDR_BASE_PAGE - STACK_BASE_PAGE }, // prot mode stack.
50 { LoaderLoadedProgram, FREELDR_BASE_PAGE, FREELDR_PAGE_COUNT }, // freeldr image
51 { LoaderFirmwareTemporary, DISKBUF_BASE_PAGE, DISKBUF_PAGE_COUNT }, // Disk read buffer for int 13h. DISKREADBUFFER
52 { LoaderFirmwareTemporary, BIOSBUF_BASE_PAGE, BIOSBUF_PAGE_COUNT }, // BIOSCALLBUFFER
53 { LoaderFirmwarePermanent, 0x9F, 0x1 }, // EBDA
54 { LoaderFirmwarePermanent, 0xA0, 0x50 }, // ROM / Video
55 { LoaderSpecialMemory, 0xF0, 0x10 }, // ROM / Video
56 { LoaderSpecialMemory, 0xFFF, 1 }, // unusable memory
57 { 0, 0, 0 }, // end of map
58 };
59
60 ULONG
61 AddMemoryDescriptor(
62 IN OUT PFREELDR_MEMORY_DESCRIPTOR List,
63 IN ULONG MaxCount,
64 IN PFN_NUMBER BasePage,
65 IN PFN_NUMBER PageCount,
66 IN TYPE_OF_MEMORY MemoryType);
67
68 static
69 BOOLEAN
70 GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemoryAtSixteenMB /* in 64KB */)
71 {
72 REGS RegsIn;
73 REGS RegsOut;
74
75 TRACE("GetExtendedMemoryConfiguration()\n");
76
77 *pMemoryAtOneMB = 0;
78 *pMemoryAtSixteenMB = 0;
79
80 // Int 15h AX=E801h
81 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
82 //
83 // AX = E801h
84 // Return:
85 // CF clear if successful
86 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
87 // BX = extended memory above 16M, in 64K blocks
88 // CX = configured memory 1M to 16M, in K
89 // DX = configured memory above 16M, in 64K blocks
90 // CF set on error
91 RegsIn.w.ax = 0xE801;
92 Int386(0x15, &RegsIn, &RegsOut);
93
94 TRACE("Int15h AX=E801h\n");
95 TRACE("AX = 0x%x\n", RegsOut.w.ax);
96 TRACE("BX = 0x%x\n", RegsOut.w.bx);
97 TRACE("CX = 0x%x\n", RegsOut.w.cx);
98 TRACE("DX = 0x%x\n", RegsOut.w.dx);
99 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
100
101 if (INT386_SUCCESS(RegsOut))
102 {
103 // If AX=BX=0000h the use CX and DX
104 if (RegsOut.w.ax == 0)
105 {
106 // Return extended memory size in K
107 *pMemoryAtSixteenMB = RegsOut.w.dx;
108 *pMemoryAtOneMB = RegsOut.w.cx;
109 return TRUE;
110 }
111 else
112 {
113 // Return extended memory size in K
114 *pMemoryAtSixteenMB = RegsOut.w.bx;
115 *pMemoryAtOneMB = RegsOut.w.ax;
116 return TRUE;
117 }
118 }
119
120 // If we get here then Int15 Func E801h didn't work
121 // So try Int15 Func 88h
122 // Int 15h AH=88h
123 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
124 //
125 // AH = 88h
126 // Return:
127 // CF clear if successful
128 // AX = number of contiguous KB starting at absolute address 100000h
129 // CF set on error
130 // AH = status
131 // 80h invalid command (PC,PCjr)
132 // 86h unsupported function (XT,PS30)
133 RegsIn.b.ah = 0x88;
134 Int386(0x15, &RegsIn, &RegsOut);
135
136 TRACE("Int15h AH=88h\n");
137 TRACE("AX = 0x%x\n", RegsOut.w.ax);
138 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
139
140 if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
141 {
142 *pMemoryAtOneMB = RegsOut.w.ax;
143 return TRUE;
144 }
145
146 // If we get here then Int15 Func 88h didn't work
147 // So try reading the CMOS
148 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x31);
149 *pMemoryAtOneMB = READ_PORT_UCHAR((PUCHAR)0x71);
150 *pMemoryAtOneMB = (*pMemoryAtOneMB & 0xFFFF);
151 *pMemoryAtOneMB = (*pMemoryAtOneMB << 8);
152
153 TRACE("Int15h Failed\n");
154 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB);
155
156 if (*pMemoryAtOneMB != 0)
157 {
158 return TRUE;
159 }
160
161 return FALSE;
162 }
163
164 static ULONG
165 PcMemGetConventionalMemorySize(VOID)
166 {
167 REGS Regs;
168
169 TRACE("GetConventionalMemorySize()\n");
170
171 /* Int 12h
172 * BIOS - GET MEMORY SIZE
173 *
174 * Return:
175 * AX = kilobytes of contiguous memory starting at absolute address 00000h
176 *
177 * This call returns the contents of the word at 0040h:0013h;
178 * in PC and XT, this value is set from the switches on the motherboard
179 */
180 Regs.w.ax = 0;
181 Int386(0x12, &Regs, &Regs);
182
183 TRACE("Int12h\n");
184 TRACE("AX = 0x%x\n\n", Regs.w.ax);
185
186 return (ULONG)Regs.w.ax;
187 }
188
189 static
190 ULONG
191 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
192 {
193 REGS Regs;
194 ULONG MapCount = 0;
195 ULONGLONG RealBaseAddress, RealSize;
196 TYPE_OF_MEMORY MemoryType;
197 ULONG Size;
198 ASSERT(PcBiosMapCount == 0);
199
200 TRACE("GetBiosMemoryMap()\n");
201
202 /* Make sure the usable memory is large enough. To do this we check the 16
203 bit value at address 0x413 inside the BDA, which gives us the usable size
204 in KB */
205 Size = (*(PUSHORT)(ULONG_PTR)0x413) * 1024;
206 if (Size < 0x9F000)
207 {
208 FrLdrBugCheckWithMessage(
209 MEMORY_INIT_FAILURE,
210 __FILE__,
211 __LINE__,
212 "The BIOS reported a usable memory range up to 0x%x, which is too small!\n",
213 Size);
214 }
215
216 /* Get the address of the Extended BIOS Data Area (EBDA).
217 * Int 15h, AH=C1h
218 * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
219 *
220 * Return:
221 * CF set on error
222 * CF clear if successful
223 * ES = segment of data area
224 */
225 Regs.x.eax = 0x0000C100;
226 Int386(0x15, &Regs, &Regs);
227
228 /* If the function fails, there is no EBDA */
229 if (INT386_SUCCESS(Regs))
230 {
231 /* Check if this is high enough */
232 ULONG EbdaBase = (ULONG)Regs.w.es << 4;
233 if (EbdaBase < 0x9F000)
234 {
235 FrLdrBugCheckWithMessage(
236 MEMORY_INIT_FAILURE,
237 __FILE__,
238 __LINE__,
239 "The location of your EBDA is 0x%lx, which is too low!\n"
240 "If you see this, please report to the ReactOS team!",
241 EbdaBase);
242 }
243
244 /* Calculate the (max) size of the EBDA */
245 Size = 0xA0000 - EbdaBase;
246
247 /* Add the descriptor */
248 MapCount = AddMemoryDescriptor(PcMemoryMap,
249 MAX_BIOS_DESCRIPTORS,
250 (EbdaBase / MM_PAGE_SIZE),
251 (Size / MM_PAGE_SIZE),
252 LoaderFirmwarePermanent);
253 }
254
255 /* Int 15h AX=E820h
256 * Newer BIOSes - GET SYSTEM MEMORY MAP
257 *
258 * AX = E820h
259 * EAX = 0000E820h
260 * EDX = 534D4150h ('SMAP')
261 * EBX = continuation value or 00000000h to start at beginning of map
262 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
263 * ES:DI -> buffer for result
264 * Return:
265 * CF clear if successful
266 * EAX = 534D4150h ('SMAP')
267 * ES:DI buffer filled
268 * EBX = next offset from which to copy or 00000000h if all done
269 * ECX = actual length returned in bytes
270 * CF set on error
271 * AH = error code (86h)
272 */
273 Regs.x.ebx = 0x00000000;
274
275 while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
276 {
277 /* Setup the registers for the BIOS call */
278 Regs.x.eax = 0x0000E820;
279 Regs.x.edx = 0x534D4150; /* ('SMAP') */
280 /* Regs.x.ebx = 0x00000001; Continuation value already set */
281 Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
282 Regs.w.es = BIOSCALLBUFSEGMENT;
283 Regs.w.di = BIOSCALLBUFOFFSET;
284 Int386(0x15, &Regs, &Regs);
285
286 TRACE("Memory Map Entry %d\n", PcBiosMapCount);
287 TRACE("Int15h AX=E820h\n");
288 TRACE("EAX = 0x%x\n", Regs.x.eax);
289 TRACE("EBX = 0x%x\n", Regs.x.ebx);
290 TRACE("ECX = 0x%x\n", Regs.x.ecx);
291 TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
292
293 /* If the BIOS didn't return 'SMAP' in EAX then
294 * it doesn't support this call. If CF is set, we're done */
295 if (Regs.x.eax != 0x534D4150 || !INT386_SUCCESS(Regs))
296 {
297 break;
298 }
299
300 /* Copy data to global buffer */
301 RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
302
303 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
304 TRACE("Length: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].Length);
305 TRACE("Type: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Type);
306 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Reserved);
307 TRACE("\n");
308
309 /* Check if this is free memory */
310 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
311 {
312 MemoryType = LoaderFree;
313
314 /* Align up base of memory area */
315 RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
316
317 /* Calculate the length after aligning the base */
318 RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
319 PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
320 RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
321 }
322 else
323 {
324 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
325 MemoryType = LoaderFirmwarePermanent;
326 else
327 MemoryType = LoaderSpecialMemory;
328
329 /* Align down base of memory area */
330 RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
331 /* Calculate the length after aligning the base */
332 RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
333 PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
334 RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
335 }
336
337 /* Check if we can add this descriptor */
338 if ((RealSize >= MM_PAGE_SIZE) && (MapCount < MaxMemoryMapSize))
339 {
340 /* Add the descriptor */
341 MapCount = AddMemoryDescriptor(PcMemoryMap,
342 MAX_BIOS_DESCRIPTORS,
343 (PFN_NUMBER)(RealBaseAddress / MM_PAGE_SIZE),
344 (PFN_NUMBER)(RealSize / MM_PAGE_SIZE),
345 MemoryType);
346 }
347
348 PcBiosMapCount++;
349
350 /* If the continuation value is zero or the
351 * carry flag is set then this was
352 * the last entry so we're done */
353 if (Regs.x.ebx == 0x00000000)
354 {
355 TRACE("End Of System Memory Map!\n\n");
356 break;
357 }
358
359 }
360
361 return MapCount;
362 }
363
364
365 PFREELDR_MEMORY_DESCRIPTOR
366 PcMemGetMemoryMap(ULONG *MemoryMapSize)
367 {
368 ULONG i, EntryCount;
369 ULONG ExtendedMemorySizeAtOneMB;
370 ULONG ExtendedMemorySizeAtSixteenMB;
371
372 EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
373
374 /* If the BIOS didn't provide a memory map, synthesize one */
375 if (0 == EntryCount)
376 {
377 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
378
379 /* Conventional memory */
380 AddMemoryDescriptor(PcMemoryMap,
381 MAX_BIOS_DESCRIPTORS,
382 0,
383 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
384 LoaderFree);
385
386 /* Extended memory */
387 EntryCount = AddMemoryDescriptor(PcMemoryMap,
388 MAX_BIOS_DESCRIPTORS,
389 1024 * 1024 / PAGE_SIZE,
390 ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
391 LoaderFree);
392
393 if (ExtendedMemorySizeAtSixteenMB != 0)
394 {
395 /* Extended memory at 16MB */
396 EntryCount = AddMemoryDescriptor(PcMemoryMap,
397 MAX_BIOS_DESCRIPTORS,
398 0x1000000 / PAGE_SIZE,
399 ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
400 LoaderFree);
401 }
402 }
403
404 TRACE("Dumping resulting memory map:\n");
405 for (i = 0; i < EntryCount; i++)
406 {
407 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
408 PcMemoryMap[i].BasePage,
409 PcMemoryMap[i].PageCount,
410 MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
411 }
412
413 *MemoryMapSize = EntryCount;
414
415 return PcMemoryMap;
416 }
417
418 /* EOF */