[FREELDR] Improve trace prints in pcmem.c, no logical changes
[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 80
31
32 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
33 ULONG PcBiosMapCount;
34
35 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1];
36 ULONG PcMapCount;
37
38 ULONG
39 AddMemoryDescriptor(
40 IN OUT PFREELDR_MEMORY_DESCRIPTOR List,
41 IN ULONG MaxCount,
42 IN PFN_NUMBER BasePage,
43 IN PFN_NUMBER PageCount,
44 IN TYPE_OF_MEMORY MemoryType);
45
46 static
47 BOOLEAN
48 GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemoryAtSixteenMB /* in 64KB */)
49 {
50 REGS RegsIn;
51 REGS RegsOut;
52
53 TRACE("GetExtendedMemoryConfiguration()\n");
54
55 *pMemoryAtOneMB = 0;
56 *pMemoryAtSixteenMB = 0;
57
58 // Int 15h AX=E801h
59 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
60 //
61 // AX = E801h
62 // Return:
63 // CF clear if successful
64 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
65 // BX = extended memory above 16M, in 64K blocks
66 // CX = configured memory 1M to 16M, in K
67 // DX = configured memory above 16M, in 64K blocks
68 // CF set on error
69 RegsIn.w.ax = 0xE801;
70 Int386(0x15, &RegsIn, &RegsOut);
71
72 TRACE("Int15h AX=E801h\n");
73 TRACE("AX = 0x%x\n", RegsOut.w.ax);
74 TRACE("BX = 0x%x\n", RegsOut.w.bx);
75 TRACE("CX = 0x%x\n", RegsOut.w.cx);
76 TRACE("DX = 0x%x\n", RegsOut.w.dx);
77 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
78
79 if (INT386_SUCCESS(RegsOut))
80 {
81 // If AX=BX=0000h the use CX and DX
82 if (RegsOut.w.ax == 0)
83 {
84 // Return extended memory size in K
85 *pMemoryAtSixteenMB = RegsOut.w.dx;
86 *pMemoryAtOneMB = RegsOut.w.cx;
87 return TRUE;
88 }
89 else
90 {
91 // Return extended memory size in K
92 *pMemoryAtSixteenMB = RegsOut.w.bx;
93 *pMemoryAtOneMB = RegsOut.w.ax;
94 return TRUE;
95 }
96 }
97
98 // If we get here then Int15 Func E801h didn't work
99 // So try Int15 Func 88h
100 // Int 15h AH=88h
101 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
102 //
103 // AH = 88h
104 // Return:
105 // CF clear if successful
106 // AX = number of contiguous KB starting at absolute address 100000h
107 // CF set on error
108 // AH = status
109 // 80h invalid command (PC,PCjr)
110 // 86h unsupported function (XT,PS30)
111 RegsIn.b.ah = 0x88;
112 Int386(0x15, &RegsIn, &RegsOut);
113
114 TRACE("Int15h AH=88h\n");
115 TRACE("AX = 0x%x\n", RegsOut.w.ax);
116 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
117
118 if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
119 {
120 *pMemoryAtOneMB = RegsOut.w.ax;
121 return TRUE;
122 }
123
124 // If we get here then Int15 Func 88h didn't work
125 // So try reading the CMOS
126 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x31);
127 *pMemoryAtOneMB = READ_PORT_UCHAR((PUCHAR)0x71);
128 *pMemoryAtOneMB = (*pMemoryAtOneMB & 0xFFFF);
129 *pMemoryAtOneMB = (*pMemoryAtOneMB << 8);
130
131 TRACE("Int15h Failed\n");
132 TRACE("CMOS reports: 0x%lx\n", *pMemoryAtOneMB);
133
134 if (*pMemoryAtOneMB != 0)
135 {
136 return TRUE;
137 }
138
139 return FALSE;
140 }
141
142 static ULONG
143 PcMemGetConventionalMemorySize(VOID)
144 {
145 REGS Regs;
146
147 TRACE("PcMemGetConventionalMemorySize()\n");
148
149 /* Int 12h
150 * BIOS - GET MEMORY SIZE
151 *
152 * Return:
153 * AX = kilobytes of contiguous memory starting at absolute address 00000h
154 *
155 * This call returns the contents of the word at 0040h:0013h;
156 * in PC and XT, this value is set from the switches on the motherboard
157 */
158 Regs.w.ax = 0;
159 Int386(0x12, &Regs, &Regs);
160
161 TRACE("Int12h\n");
162 TRACE("AX = 0x%x\n\n", Regs.w.ax);
163
164 return (ULONG)Regs.w.ax;
165 }
166
167 static
168 BOOLEAN
169 GetEbdaLocation(
170 PULONG BaseAddress,
171 PULONG Size)
172 {
173 REGS Regs;
174
175 /* Get the address of the Extended BIOS Data Area (EBDA).
176 * Int 15h, AH=C1h
177 * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
178 *
179 * Return:
180 * CF set on error
181 * CF clear if successful
182 * ES = segment of data area
183 */
184 Regs.x.eax = 0x0000C100;
185 Int386(0x15, &Regs, &Regs);
186
187 /* If the function fails, there is no EBDA */
188 if (!INT386_SUCCESS(Regs))
189 {
190 return FALSE;
191 }
192
193 /* Get Base address and (maximum) size */
194 *BaseAddress = (ULONG)Regs.w.es << 4;
195 *Size = 0xA0000 - *BaseAddress;
196 return TRUE;
197 }
198
199 static
200 ULONG
201 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
202 {
203 REGS Regs;
204 ULONGLONG RealBaseAddress, EndAddress, RealSize;
205 TYPE_OF_MEMORY MemoryType;
206 ULONG Size, RequiredSize;
207
208 ASSERT(PcBiosMapCount == 0);
209
210 TRACE("PcMemGetBiosMemoryMap()\n");
211
212 /* Make sure the usable memory is large enough. To do this we check the 16
213 bit value at address 0x413 inside the BDA, which gives us the usable size
214 in KB */
215 Size = (*(PUSHORT)(ULONG_PTR)0x413) * 1024;
216 RequiredSize = FREELDR_BASE + FrLdrImageSize + PAGE_SIZE;
217 if (Size < RequiredSize)
218 {
219 FrLdrBugCheckWithMessage(
220 MEMORY_INIT_FAILURE,
221 __FILE__,
222 __LINE__,
223 "The BIOS reported a usable memory range up to 0x%lx, which is too small!\n"
224 "Required size is 0x%lx\n\n"
225 "If you see this, please report to the ReactOS team!",
226 Size, RequiredSize);
227 }
228
229 /* Int 15h AX=E820h
230 * Newer BIOSes - GET SYSTEM MEMORY MAP
231 *
232 * AX = E820h
233 * EAX = 0000E820h
234 * EDX = 534D4150h ('SMAP')
235 * EBX = continuation value or 00000000h to start at beginning of map
236 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
237 * ES:DI -> buffer for result
238 * Return:
239 * CF clear if successful
240 * EAX = 534D4150h ('SMAP')
241 * ES:DI buffer filled
242 * EBX = next offset from which to copy or 00000000h if all done
243 * ECX = actual length returned in bytes
244 * CF set on error
245 * AH = error code (86h)
246 */
247 Regs.x.ebx = 0x00000000;
248
249 while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
250 {
251 /* Setup the registers for the BIOS call */
252 Regs.x.eax = 0x0000E820;
253 Regs.x.edx = 0x534D4150; /* ('SMAP') */
254 /* Regs.x.ebx = 0x00000001; Continuation value already set */
255 Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
256 Regs.w.es = BIOSCALLBUFSEGMENT;
257 Regs.w.di = BIOSCALLBUFOFFSET;
258 Int386(0x15, &Regs, &Regs);
259
260 TRACE("Memory Map Entry %lu\n", PcBiosMapCount);
261 TRACE("Int15h AX=E820h\n");
262 TRACE("EAX = 0x%lx\n", Regs.x.eax);
263 TRACE("EBX = 0x%lx\n", Regs.x.ebx);
264 TRACE("ECX = 0x%lx\n", Regs.x.ecx);
265 TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
266
267 /* If the BIOS didn't return 'SMAP' in EAX then
268 * it doesn't support this call. */
269 if (Regs.x.eax != 0x534D4150)
270 {
271 WARN("BIOS doesn't support Int15h AX=E820h!\n\n");
272 break;
273 }
274
275 /* If the carry flag is set,
276 * then this call was past the last entry, so we're done. */
277 if (!INT386_SUCCESS(Regs))
278 {
279 TRACE("End of System Memory Map! (Past last)\n\n");
280 break;
281 }
282
283 /* Copy data to global buffer */
284 RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
285
286 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
287 TRACE("Length: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].Length);
288 TRACE("Type: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Type);
289 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Reserved);
290 TRACE("\n");
291
292 /* Check if this is free memory */
293 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
294 {
295 MemoryType = LoaderFree;
296
297 /* Align up base of memory range */
298 RealBaseAddress = ALIGN_UP_BY(PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
299 PAGE_SIZE);
300
301 /* Calculate aligned EndAddress */
302 EndAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
303 PcBiosMemoryMap[PcBiosMapCount].Length;
304 EndAddress = ALIGN_DOWN_BY(EndAddress, PAGE_SIZE);
305
306 /* Check if there is anything left */
307 if (EndAddress <= RealBaseAddress)
308 {
309 /* This doesn't span any page, so continue with next range */
310 TRACE("Skipping aligned range < PAGE_SIZE. (PcBiosMapCount = %lu, BaseAddress = %lu, Length = %lu)\n",
311 PcBiosMapCount,
312 PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
313 PcBiosMemoryMap[PcBiosMapCount].Length);
314 continue;
315 }
316
317 /* Calculate the length of the aligned range */
318 RealSize = EndAddress - RealBaseAddress;
319 }
320 else
321 {
322 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
323 MemoryType = LoaderFirmwarePermanent;
324 else
325 MemoryType = LoaderSpecialMemory;
326
327 /* Align down base of memory area */
328 RealBaseAddress = ALIGN_DOWN_BY(PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
329 PAGE_SIZE);
330
331 /* Calculate the length after aligning the base */
332 RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
333 PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
334 RealSize = ALIGN_UP_BY(RealSize, PAGE_SIZE);
335 }
336
337 /* Check if we can add this descriptor */
338 if (RealSize < MM_PAGE_SIZE)
339 {
340 TRACE("Skipping aligned range < MM_PAGE_SIZE. (PcBiosMapCount = %lu, BaseAddress = %lu, Length = %lu)\n",
341 PcBiosMapCount,
342 PcBiosMemoryMap[PcBiosMapCount].BaseAddress,
343 PcBiosMemoryMap[PcBiosMapCount].Length);
344 }
345 else if (PcMapCount >= MaxMemoryMapSize)
346 {
347 ERR("PcMemoryMap is already full! (PcBiosMapCount = %lu, PcMapCount = %lu (>= %lu))\n",
348 PcBiosMapCount, PcMapCount, MaxMemoryMapSize);
349 }
350 else
351 {
352 /* Add the descriptor */
353 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
354 MAX_BIOS_DESCRIPTORS,
355 (PFN_NUMBER)(RealBaseAddress / MM_PAGE_SIZE),
356 (PFN_NUMBER)(RealSize / MM_PAGE_SIZE),
357 MemoryType);
358 }
359
360 PcBiosMapCount++;
361
362 /* If the continuation value is zero,
363 * then this was the last entry, so we're done. */
364 if (Regs.x.ebx == 0x00000000)
365 {
366 TRACE("End of System Memory Map! (Reset)\n\n");
367 break;
368 }
369 }
370
371 TRACE("PcMemGetBiosMemoryMap end: PcBiosMapCount = %lu\n", PcBiosMapCount);
372 return PcBiosMapCount;
373 }
374
375 VOID
376 ReserveMemory(
377 ULONG_PTR BaseAddress,
378 SIZE_T Size,
379 TYPE_OF_MEMORY MemoryType,
380 PCHAR Usage)
381 {
382 ULONG_PTR BasePage, PageCount;
383 ULONG i;
384
385 BasePage = BaseAddress / PAGE_SIZE;
386 PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress, Size);
387
388 for (i = 0; i < PcMapCount; i++)
389 {
390 /* Check for conflicting descriptor */
391 if ((PcMemoryMap[i].BasePage < BasePage + PageCount) &&
392 (PcMemoryMap[i].BasePage + PcMemoryMap[i].PageCount > BasePage))
393 {
394 /* Check if the memory is free */
395 if (PcMemoryMap[i].MemoryType != LoaderFree)
396 {
397 FrLdrBugCheckWithMessage(
398 MEMORY_INIT_FAILURE,
399 __FILE__,
400 __LINE__,
401 "Failed to reserve memory in the range 0x%Ix - 0x%Ix for %s",
402 BaseAddress,
403 Size,
404 Usage);
405 }
406 }
407 }
408
409 /* Add the memory descriptor */
410 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
411 MAX_BIOS_DESCRIPTORS,
412 BasePage,
413 PageCount,
414 MemoryType);
415 }
416
417 VOID
418 SetMemory(
419 ULONG_PTR BaseAddress,
420 SIZE_T Size,
421 TYPE_OF_MEMORY MemoryType)
422 {
423 ULONG_PTR BasePage, PageCount;
424
425 BasePage = BaseAddress / PAGE_SIZE;
426 PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress, Size);
427
428 /* Add the memory descriptor */
429 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
430 MAX_BIOS_DESCRIPTORS,
431 BasePage,
432 PageCount,
433 MemoryType);
434 }
435
436 PFREELDR_MEMORY_DESCRIPTOR
437 PcMemGetMemoryMap(ULONG *MemoryMapSize)
438 {
439 ULONG i, EntryCount;
440 ULONG ExtendedMemorySizeAtOneMB;
441 ULONG ExtendedMemorySizeAtSixteenMB;
442 ULONG EbdaBase, EbdaSize;
443
444 TRACE("PcMemGetMemoryMap()\n");
445
446 EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
447
448 /* If the BIOS didn't provide a memory map, synthesize one */
449 if (EntryCount == 0)
450 {
451 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB,
452 &ExtendedMemorySizeAtSixteenMB);
453
454 /* Conventional memory */
455 AddMemoryDescriptor(PcMemoryMap,
456 MAX_BIOS_DESCRIPTORS,
457 0,
458 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
459 LoaderFree);
460
461 /* Extended memory */
462 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
463 MAX_BIOS_DESCRIPTORS,
464 1024 * 1024 / PAGE_SIZE,
465 ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
466 LoaderFree);
467
468 if (ExtendedMemorySizeAtSixteenMB != 0)
469 {
470 /* Extended memory at 16MB */
471 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
472 MAX_BIOS_DESCRIPTORS,
473 0x1000000 / PAGE_SIZE,
474 ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
475 LoaderFree);
476 }
477
478 /* Check if we have an EBDA and get it's location */
479 if (GetEbdaLocation(&EbdaBase, &EbdaSize))
480 {
481 /* Add the descriptor */
482 PcMapCount = AddMemoryDescriptor(PcMemoryMap,
483 MAX_BIOS_DESCRIPTORS,
484 (EbdaBase / PAGE_SIZE),
485 ADDRESS_AND_SIZE_TO_SPAN_PAGES(EbdaBase, EbdaSize),
486 LoaderFirmwarePermanent);
487 }
488 }
489
490 /* Setup some protected ranges */
491 SetMemory(0x000000, 0x01000, LoaderFirmwarePermanent); // Realmode IVT / BDA
492 SetMemory(0x0A0000, 0x50000, LoaderFirmwarePermanent); // Video memory
493 SetMemory(0x0F0000, 0x10000, LoaderSpecialMemory); // ROM
494 SetMemory(0xFFF000, 0x01000, LoaderSpecialMemory); // unusable memory (do we really need this?)
495
496 /* Reserve some static ranges for freeldr */
497 ReserveMemory(0x1000, STACKLOW - 0x1000, LoaderFirmwareTemporary, "BIOS area");
498 ReserveMemory(STACKLOW, STACKADDR - STACKLOW, LoaderOsloaderStack, "FreeLdr stack");
499 ReserveMemory(FREELDR_BASE, FrLdrImageSize, LoaderLoadedProgram, "FreeLdr image");
500
501 /* Default to 1 page above freeldr for the disk read buffer */
502 DiskReadBuffer = (PUCHAR)ALIGN_UP_BY(FREELDR_BASE + FrLdrImageSize, PAGE_SIZE);
503 DiskReadBufferSize = PAGE_SIZE;
504
505 /* Scan for free range above freeldr image */
506 for (i = 0; i < PcMapCount; i++)
507 {
508 if ((PcMemoryMap[i].BasePage > (FREELDR_BASE / PAGE_SIZE)) &&
509 (PcMemoryMap[i].MemoryType == LoaderFree))
510 {
511 /* Use this range for the disk read buffer */
512 DiskReadBuffer = (PVOID)(PcMemoryMap[i].BasePage * PAGE_SIZE);
513 DiskReadBufferSize = min(PcMemoryMap[i].PageCount * PAGE_SIZE,
514 MAX_DISKREADBUFFER_SIZE);
515 break;
516 }
517 }
518
519 TRACE("DiskReadBuffer=%p, DiskReadBufferSize=%lx\n",
520 DiskReadBuffer, DiskReadBufferSize);
521
522 /* Now reserve the range for the disk read buffer */
523 ReserveMemory((ULONG_PTR)DiskReadBuffer,
524 DiskReadBufferSize,
525 LoaderFirmwareTemporary,
526 "Disk read buffer");
527
528 TRACE("Dumping resulting memory map:\n");
529 for (i = 0; i < PcMapCount; i++)
530 {
531 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
532 PcMemoryMap[i].BasePage,
533 PcMemoryMap[i].PageCount,
534 MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
535 }
536
537 *MemoryMapSize = PcMapCount;
538 return PcMemoryMap;
539 }
540
541
542 /* EOF */