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 32
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)
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)
42 BIOS_MEMORY_MAP PcBiosMemoryMap
[MAX_BIOS_DESCRIPTORS
];
45 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap
[MAX_BIOS_DESCRIPTORS
+ 1] =
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
62 IN OUT PFREELDR_MEMORY_DESCRIPTOR List
,
64 IN PFN_NUMBER BasePage
,
65 IN PFN_NUMBER PageCount
,
66 IN TYPE_OF_MEMORY MemoryType
);
70 GetExtendedMemoryConfiguration(ULONG
* pMemoryAtOneMB
/* in KB */, ULONG
* pMemoryAtSixteenMB
/* in 64KB */)
75 TRACE("GetExtendedMemoryConfiguration()\n");
78 *pMemoryAtSixteenMB
= 0;
81 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
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
92 Int386(0x15, &RegsIn
, &RegsOut
);
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");
101 if (INT386_SUCCESS(RegsOut
))
103 // If AX=BX=0000h the use CX and DX
104 if (RegsOut
.w
.ax
== 0)
106 // Return extended memory size in K
107 *pMemoryAtSixteenMB
= RegsOut
.w
.dx
;
108 *pMemoryAtOneMB
= RegsOut
.w
.cx
;
113 // Return extended memory size in K
114 *pMemoryAtSixteenMB
= RegsOut
.w
.bx
;
115 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
120 // If we get here then Int15 Func E801h didn't work
121 // So try Int15 Func 88h
123 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
127 // CF clear if successful
128 // AX = number of contiguous KB starting at absolute address 100000h
131 // 80h invalid command (PC,PCjr)
132 // 86h unsupported function (XT,PS30)
134 Int386(0x15, &RegsIn
, &RegsOut
);
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");
140 if (INT386_SUCCESS(RegsOut
) && RegsOut
.w
.ax
!= 0)
142 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
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);
153 TRACE("Int15h Failed\n");
154 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB
);
156 if (*pMemoryAtOneMB
!= 0)
165 PcMemGetConventionalMemorySize(VOID
)
169 TRACE("GetConventionalMemorySize()\n");
172 * BIOS - GET MEMORY SIZE
175 * AX = kilobytes of contiguous memory starting at absolute address 00000h
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
181 Int386(0x12, &Regs
, &Regs
);
184 TRACE("AX = 0x%x\n\n", Regs
.w
.ax
);
186 return (ULONG
)Regs
.w
.ax
;
191 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap
, ULONG MaxMemoryMapSize
)
195 ULONGLONG RealBaseAddress
, RealSize
;
196 TYPE_OF_MEMORY MemoryType
;
198 ASSERT(PcBiosMapCount
== 0);
200 TRACE("GetBiosMemoryMap()\n");
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
205 Size
= (*(PUSHORT
)(ULONG_PTR
)0x413) * 1024;
208 FrLdrBugCheckWithMessage(
212 "The BIOS reported a usable memory range up to 0x%x, which is too small!\n",
216 /* Get the address of the Extended BIOS Data Area (EBDA).
218 * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
222 * CF clear if successful
223 * ES = segment of data area
225 Regs
.x
.eax
= 0x0000C100;
226 Int386(0x15, &Regs
, &Regs
);
228 /* If the function fails, there is no EBDA */
229 if (INT386_SUCCESS(Regs
))
231 /* Check if this is high enough */
232 ULONG EbdaBase
= (ULONG
)Regs
.w
.es
<< 4;
233 if (EbdaBase
< 0x9F000)
235 FrLdrBugCheckWithMessage(
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!",
244 /* Calculate the (max) size of the EBDA */
245 Size
= 0xA0000 - EbdaBase
;
247 /* Add the descriptor */
248 MapCount
= AddMemoryDescriptor(PcMemoryMap
,
249 MAX_BIOS_DESCRIPTORS
,
250 (EbdaBase
/ MM_PAGE_SIZE
),
251 (Size
/ MM_PAGE_SIZE
),
252 LoaderFirmwarePermanent
);
256 * Newer BIOSes - GET SYSTEM MEMORY MAP
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
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
271 * AH = error code (86h)
273 Regs
.x
.ebx
= 0x00000000;
275 while (PcBiosMapCount
< MAX_BIOS_DESCRIPTORS
)
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
);
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");
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
))
300 /* Copy data to global buffer */
301 RtlCopyMemory(&PcBiosMemoryMap
[PcBiosMapCount
], (PVOID
)BIOSCALLBUFFER
, Regs
.x
.ecx
);
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
);
309 /* Check if this is free memory */
310 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryUsable
)
312 MemoryType
= LoaderFree
;
314 /* Align up base of memory area */
315 RealBaseAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
& ~(MM_PAGE_SIZE
- 1ULL);
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);
324 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryReserved
)
325 MemoryType
= LoaderFirmwarePermanent
;
327 MemoryType
= LoaderSpecialMemory
;
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);
337 /* Check if we can add this descriptor */
338 if ((RealSize
>= MM_PAGE_SIZE
) && (MapCount
< MaxMemoryMapSize
))
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
),
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)
355 TRACE("End Of System Memory Map!\n\n");
365 PFREELDR_MEMORY_DESCRIPTOR
366 PcMemGetMemoryMap(ULONG
*MemoryMapSize
)
369 ULONG ExtendedMemorySizeAtOneMB
;
370 ULONG ExtendedMemorySizeAtSixteenMB
;
372 EntryCount
= PcMemGetBiosMemoryMap(PcMemoryMap
, MAX_BIOS_DESCRIPTORS
);
374 /* If the BIOS didn't provide a memory map, synthesize one */
377 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB
, &ExtendedMemorySizeAtSixteenMB
);
379 /* Conventional memory */
380 AddMemoryDescriptor(PcMemoryMap
,
381 MAX_BIOS_DESCRIPTORS
,
383 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE
,
386 /* Extended memory */
387 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
388 MAX_BIOS_DESCRIPTORS
,
389 1024 * 1024 / PAGE_SIZE
,
390 ExtendedMemorySizeAtOneMB
* 1024 / PAGE_SIZE
,
393 if (ExtendedMemorySizeAtSixteenMB
!= 0)
395 /* Extended memory at 16MB */
396 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
397 MAX_BIOS_DESCRIPTORS
,
398 0x1000000 / PAGE_SIZE
,
399 ExtendedMemorySizeAtSixteenMB
* 64 * 1024 / PAGE_SIZE
,
404 TRACE("Dumping resulting memory map:\n");
405 for (i
= 0; i
< EntryCount
; i
++)
407 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
408 PcMemoryMap
[i
].BasePage
,
409 PcMemoryMap
[i
].PageCount
,
410 MmGetSystemMemoryMapTypeString(PcMemoryMap
[i
].MemoryType
));
413 *MemoryMapSize
= EntryCount
;