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.
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.
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.
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>
23 #include <arch/pc/x86common.h>
28 DBG_DEFAULT_CHANNEL(MEMORY
);
30 #define MAX_BIOS_DESCRIPTORS 80
32 BIOS_MEMORY_MAP PcBiosMemoryMap
[MAX_BIOS_DESCRIPTORS
];
35 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap
[MAX_BIOS_DESCRIPTORS
+ 1];
40 IN OUT PFREELDR_MEMORY_DESCRIPTOR List
,
42 IN PFN_NUMBER BasePage
,
43 IN PFN_NUMBER PageCount
,
44 IN TYPE_OF_MEMORY MemoryType
);
48 GetExtendedMemoryConfiguration(ULONG
* pMemoryAtOneMB
/* in KB */, ULONG
* pMemoryAtSixteenMB
/* in 64KB */)
53 TRACE("GetExtendedMemoryConfiguration()\n");
56 *pMemoryAtSixteenMB
= 0;
59 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
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
70 Int386(0x15, &RegsIn
, &RegsOut
);
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");
79 if (INT386_SUCCESS(RegsOut
))
81 // If AX=BX=0000h the use CX and DX
82 if (RegsOut
.w
.ax
== 0)
84 // Return extended memory size in K
85 *pMemoryAtSixteenMB
= RegsOut
.w
.dx
;
86 *pMemoryAtOneMB
= RegsOut
.w
.cx
;
91 // Return extended memory size in K
92 *pMemoryAtSixteenMB
= RegsOut
.w
.bx
;
93 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
98 // If we get here then Int15 Func E801h didn't work
99 // So try Int15 Func 88h
101 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
105 // CF clear if successful
106 // AX = number of contiguous KB starting at absolute address 100000h
109 // 80h invalid command (PC,PCjr)
110 // 86h unsupported function (XT,PS30)
112 Int386(0x15, &RegsIn
, &RegsOut
);
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");
118 if (INT386_SUCCESS(RegsOut
) && RegsOut
.w
.ax
!= 0)
120 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
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);
131 TRACE("Int15h Failed\n");
132 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB
);
134 if (*pMemoryAtOneMB
!= 0)
143 PcMemGetConventionalMemorySize(VOID
)
147 TRACE("GetConventionalMemorySize()\n");
150 * BIOS - GET MEMORY SIZE
153 * AX = kilobytes of contiguous memory starting at absolute address 00000h
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
159 Int386(0x12, &Regs
, &Regs
);
162 TRACE("AX = 0x%x\n\n", Regs
.w
.ax
);
164 return (ULONG
)Regs
.w
.ax
;
175 /* Get the address of the Extended BIOS Data Area (EBDA).
177 * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
181 * CF clear if successful
182 * ES = segment of data area
184 Regs
.x
.eax
= 0x0000C100;
185 Int386(0x15, &Regs
, &Regs
);
187 /* If the function fails, there is no EBDA */
188 if (!INT386_SUCCESS(Regs
))
193 /* Get Base address and (maximum) size */
194 *BaseAddress
= (ULONG
)Regs
.w
.es
<< 4;
195 *Size
= 0xA0000 - *BaseAddress
;
201 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap
, ULONG MaxMemoryMapSize
)
204 ULONGLONG RealBaseAddress
, EndAddress
, RealSize
;
205 TYPE_OF_MEMORY MemoryType
;
206 ULONG Size
, RequiredSize
;
207 ASSERT(PcBiosMapCount
== 0);
209 TRACE("GetBiosMemoryMap()\n");
211 /* Make sure the usable memory is large enough. To do this we check the 16
212 bit value at address 0x413 inside the BDA, which gives us the usable size
214 Size
= (*(PUSHORT
)(ULONG_PTR
)0x413) * 1024;
215 RequiredSize
= FREELDR_BASE
+ FrLdrImageSize
+ PAGE_SIZE
;
216 if (Size
< RequiredSize
)
218 FrLdrBugCheckWithMessage(
222 "The BIOS reported a usable memory range up to 0x%x, which is too small!\n"
223 "Required size is 0x%x\n\n"
224 "If you see this, please report to the ReactOS team!",
229 * Newer BIOSes - GET SYSTEM MEMORY MAP
233 * EDX = 534D4150h ('SMAP')
234 * EBX = continuation value or 00000000h to start at beginning of map
235 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
236 * ES:DI -> buffer for result
238 * CF clear if successful
239 * EAX = 534D4150h ('SMAP')
240 * ES:DI buffer filled
241 * EBX = next offset from which to copy or 00000000h if all done
242 * ECX = actual length returned in bytes
244 * AH = error code (86h)
246 Regs
.x
.ebx
= 0x00000000;
248 while (PcBiosMapCount
< MAX_BIOS_DESCRIPTORS
)
250 /* Setup the registers for the BIOS call */
251 Regs
.x
.eax
= 0x0000E820;
252 Regs
.x
.edx
= 0x534D4150; /* ('SMAP') */
253 /* Regs.x.ebx = 0x00000001; Continuation value already set */
254 Regs
.x
.ecx
= sizeof(BIOS_MEMORY_MAP
);
255 Regs
.w
.es
= BIOSCALLBUFSEGMENT
;
256 Regs
.w
.di
= BIOSCALLBUFOFFSET
;
257 Int386(0x15, &Regs
, &Regs
);
259 TRACE("Memory Map Entry %d\n", PcBiosMapCount
);
260 TRACE("Int15h AX=E820h\n");
261 TRACE("EAX = 0x%x\n", Regs
.x
.eax
);
262 TRACE("EBX = 0x%x\n", Regs
.x
.ebx
);
263 TRACE("ECX = 0x%x\n", Regs
.x
.ecx
);
264 TRACE("CF set = %s\n", (Regs
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
266 /* If the BIOS didn't return 'SMAP' in EAX then
267 * it doesn't support this call. If CF is set, we're done */
268 if (Regs
.x
.eax
!= 0x534D4150 || !INT386_SUCCESS(Regs
))
273 /* Copy data to global buffer */
274 RtlCopyMemory(&PcBiosMemoryMap
[PcBiosMapCount
], (PVOID
)BIOSCALLBUFFER
, Regs
.x
.ecx
);
276 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
);
277 TRACE("Length: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].Length
);
278 TRACE("Type: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Type
);
279 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Reserved
);
282 /* Check if this is free memory */
283 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryUsable
)
285 MemoryType
= LoaderFree
;
287 /* Align up base of memory range */
288 RealBaseAddress
= ALIGN_UP_BY(PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
,
291 /* Calculate aligned EndAddress */
292 EndAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
293 PcBiosMemoryMap
[PcBiosMapCount
].Length
;
294 EndAddress
= ALIGN_DOWN_BY(EndAddress
, PAGE_SIZE
);
296 /* Check if there is anything left */
297 if (EndAddress
<= RealBaseAddress
)
299 /* This doesn't span any page, so continue with next range */
303 /* Calculate the length of the aligned range */
304 RealSize
= EndAddress
- RealBaseAddress
;
308 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryReserved
)
309 MemoryType
= LoaderFirmwarePermanent
;
311 MemoryType
= LoaderSpecialMemory
;
313 /* Align down base of memory area */
314 RealBaseAddress
= ALIGN_DOWN_BY(PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
,
317 /* Calculate the length after aligning the base */
318 RealSize
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
319 PcBiosMemoryMap
[PcBiosMapCount
].Length
- RealBaseAddress
;
320 RealSize
= ALIGN_UP_BY(RealSize
, PAGE_SIZE
);
323 /* Check if we can add this descriptor */
324 if ((RealSize
>= MM_PAGE_SIZE
) && (PcMapCount
< MaxMemoryMapSize
))
326 /* Add the descriptor */
327 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
328 MAX_BIOS_DESCRIPTORS
,
329 (PFN_NUMBER
)(RealBaseAddress
/ MM_PAGE_SIZE
),
330 (PFN_NUMBER
)(RealSize
/ MM_PAGE_SIZE
),
336 /* If the continuation value is zero or the
337 * carry flag is set then this was
338 * the last entry so we're done */
339 if (Regs
.x
.ebx
== 0x00000000)
341 TRACE("End Of System Memory Map!\n\n");
346 TRACE("GetBiosMemoryMap end, PcBiosMapCount = %ld\n", PcBiosMapCount
);
347 return PcBiosMapCount
;
352 ULONG_PTR BaseAddress
,
354 TYPE_OF_MEMORY MemoryType
,
357 ULONG_PTR BasePage
, PageCount
;
360 BasePage
= BaseAddress
/ PAGE_SIZE
;
361 PageCount
= ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress
, Size
);
363 for (i
= 0; i
< PcMapCount
; i
++)
365 /* Check for conflicting descriptor */
366 if ((PcMemoryMap
[i
].BasePage
< BasePage
+ PageCount
) &&
367 (PcMemoryMap
[i
].BasePage
+ PcMemoryMap
[i
].PageCount
> BasePage
))
369 /* Check if the memory is free */
370 if (PcMemoryMap
[i
].MemoryType
!= LoaderFree
)
372 FrLdrBugCheckWithMessage(
376 "Failed to reserve memory in the range 0x%Ix - 0x%Ix for %s",
384 /* Add the memory descriptor */
385 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
386 MAX_BIOS_DESCRIPTORS
,
394 ULONG_PTR BaseAddress
,
396 TYPE_OF_MEMORY MemoryType
)
398 ULONG_PTR BasePage
, PageCount
;
400 BasePage
= BaseAddress
/ PAGE_SIZE
;
401 PageCount
= ADDRESS_AND_SIZE_TO_SPAN_PAGES(BaseAddress
, Size
);
403 /* Add the memory descriptor */
404 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
405 MAX_BIOS_DESCRIPTORS
,
411 PFREELDR_MEMORY_DESCRIPTOR
412 PcMemGetMemoryMap(ULONG
*MemoryMapSize
)
415 ULONG ExtendedMemorySizeAtOneMB
;
416 ULONG ExtendedMemorySizeAtSixteenMB
;
417 ULONG EbdaBase
, EbdaSize
;
418 TRACE("PcMemGetMemoryMap()\n");
420 EntryCount
= PcMemGetBiosMemoryMap(PcMemoryMap
, MAX_BIOS_DESCRIPTORS
);
422 /* If the BIOS didn't provide a memory map, synthesize one */
425 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB
,
426 &ExtendedMemorySizeAtSixteenMB
);
428 /* Conventional memory */
429 AddMemoryDescriptor(PcMemoryMap
,
430 MAX_BIOS_DESCRIPTORS
,
432 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE
,
435 /* Extended memory */
436 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
437 MAX_BIOS_DESCRIPTORS
,
438 1024 * 1024 / PAGE_SIZE
,
439 ExtendedMemorySizeAtOneMB
* 1024 / PAGE_SIZE
,
442 if (ExtendedMemorySizeAtSixteenMB
!= 0)
444 /* Extended memory at 16MB */
445 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
446 MAX_BIOS_DESCRIPTORS
,
447 0x1000000 / PAGE_SIZE
,
448 ExtendedMemorySizeAtSixteenMB
* 64 * 1024 / PAGE_SIZE
,
452 /* Check if we have an EBDA and get it's location */
453 if (GetEbdaLocation(&EbdaBase
, &EbdaSize
))
455 /* Add the descriptor */
456 PcMapCount
= AddMemoryDescriptor(PcMemoryMap
,
457 MAX_BIOS_DESCRIPTORS
,
458 (EbdaBase
/ PAGE_SIZE
),
459 ADDRESS_AND_SIZE_TO_SPAN_PAGES(EbdaBase
, EbdaSize
),
460 LoaderFirmwarePermanent
);
464 /* Setup some protected ranges */
465 SetMemory(0x000000, 0x01000, LoaderFirmwarePermanent
); // Realmode IVT / BDA
466 SetMemory(0x0A0000, 0x50000, LoaderFirmwarePermanent
); // Video memory
467 SetMemory(0x0F0000, 0x10000, LoaderSpecialMemory
); // ROM
468 SetMemory(0xFFF000, 0x01000, LoaderSpecialMemory
); // unusable memory (do we really need this?)
470 /* Reserve some static ranges for freeldr */
471 ReserveMemory(0x1000, STACKLOW
- 0x1000, LoaderFirmwareTemporary
, "BIOS area");
472 ReserveMemory(STACKLOW
, STACKADDR
- STACKLOW
, LoaderOsloaderStack
, "FreeLdr stack");
473 ReserveMemory(FREELDR_BASE
, FrLdrImageSize
, LoaderLoadedProgram
, "FreeLdr image");
475 /* Default to 1 page above freeldr for the disk read buffer */
476 DiskReadBuffer
= (PUCHAR
)ALIGN_UP_BY(FREELDR_BASE
+ FrLdrImageSize
, PAGE_SIZE
);
477 DiskReadBufferSize
= PAGE_SIZE
;
479 /* Scan for free range above freeldr image */
480 for (i
= 0; i
< PcMapCount
; i
++)
482 if ((PcMemoryMap
[i
].BasePage
> (FREELDR_BASE
/ PAGE_SIZE
)) &&
483 (PcMemoryMap
[i
].MemoryType
== LoaderFree
))
485 /* Use this range for the disk read buffer */
486 DiskReadBuffer
= (PVOID
)(PcMemoryMap
[i
].BasePage
* PAGE_SIZE
);
487 DiskReadBufferSize
= min(PcMemoryMap
[i
].PageCount
* PAGE_SIZE
,
488 MAX_DISKREADBUFFER_SIZE
);
493 TRACE("DiskReadBuffer=%p, DiskReadBufferSize=%lx\n",
494 DiskReadBuffer
, DiskReadBufferSize
);
496 /* Now reserve the range for the disk read buffer */
497 ReserveMemory((ULONG_PTR
)DiskReadBuffer
,
499 LoaderFirmwareTemporary
,
502 TRACE("Dumping resulting memory map:\n");
503 for (i
= 0; i
< PcMapCount
; i
++)
505 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
506 PcMemoryMap
[i
].BasePage
,
507 PcMemoryMap
[i
].PageCount
,
508 MmGetSystemMemoryMapTypeString(PcMemoryMap
[i
].MemoryType
));
511 *MemoryMapSize
= PcMapCount
;