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
31 #define FREELDR_BASE_PAGE (FREELDR_BASE / PAGE_SIZE)
32 #define FILEBUF_BASE_PAGE (FILESYSBUFFER / PAGE_SIZE)
33 #define DISKBUF_BASE_PAGE (DISKREADBUFFER / PAGE_SIZE)
34 #define STACK_BASE_PAGE (DISKBUF_BASE_PAGE + 1)
35 #define STACK_END_PAGE (STACK32ADDR / PAGE_SIZE)
36 #define BIOSBUF_BASE_PAGE (BIOSCALLBUFFER / PAGE_SIZE)
38 #define FREELDR_PAGE_COUNT (FILEBUF_BASE_PAGE - FREELDR_BASE_PAGE)
39 #define FILEBUF_PAGE_COUNT (DISKBUF_BASE_PAGE - FILEBUF_BASE_PAGE)
40 #define DISKBUF_PAGE_COUNT (1)
41 #define STACK_PAGE_COUNT (STACK_END_PAGE - STACK_BASE_PAGE)
42 #define BIOSBUF_PAGE_COUNT (1)
44 BIOS_MEMORY_MAP PcBiosMemoryMap
[MAX_BIOS_DESCRIPTORS
];
47 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap
[MAX_BIOS_DESCRIPTORS
+ 1] =
49 { LoaderFirmwarePermanent
, 0x00, 1 }, // realmode int vectors
50 { LoaderFirmwareTemporary
, 0x01, FREELDR_BASE_PAGE
- 1 }, // freeldr stack + cmdline
51 { LoaderLoadedProgram
, FREELDR_BASE_PAGE
, FREELDR_PAGE_COUNT
}, // freeldr image
52 { LoaderFirmwareTemporary
, FILEBUF_BASE_PAGE
, FILEBUF_PAGE_COUNT
}, // File system read buffer. FILESYSBUFFER
53 { LoaderFirmwareTemporary
, DISKBUF_BASE_PAGE
, DISKBUF_PAGE_COUNT
}, // Disk read buffer for int 13h. DISKREADBUFFER
54 { LoaderOsloaderStack
, STACK_BASE_PAGE
, STACK_PAGE_COUNT
}, // prot mode stack.
55 { LoaderFirmwareTemporary
, BIOSBUF_BASE_PAGE
, BIOSBUF_PAGE_COUNT
}, // BIOSCALLBUFFER
56 { LoaderFirmwarePermanent
, 0xA0, 0x50 }, // ROM / Video
57 { LoaderSpecialMemory
, 0xF0, 0x10 }, // ROM / Video
58 { LoaderSpecialMemory
, 0xFFF, 1 }, // unusable memory
59 { 0, 0, 0 }, // end of map
64 IN OUT PFREELDR_MEMORY_DESCRIPTOR List
,
66 IN PFN_NUMBER BasePage
,
67 IN PFN_NUMBER PageCount
,
68 IN TYPE_OF_MEMORY MemoryType
);
72 GetExtendedMemoryConfiguration(ULONG
* pMemoryAtOneMB
/* in KB */, ULONG
* pMemoryAtSixteenMB
/* in 64KB */)
77 TRACE("GetExtendedMemoryConfiguration()\n");
80 *pMemoryAtSixteenMB
= 0;
83 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
87 // CF clear if successful
88 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
89 // BX = extended memory above 16M, in 64K blocks
90 // CX = configured memory 1M to 16M, in K
91 // DX = configured memory above 16M, in 64K blocks
94 Int386(0x15, &RegsIn
, &RegsOut
);
96 TRACE("Int15h AX=E801h\n");
97 TRACE("AX = 0x%x\n", RegsOut
.w
.ax
);
98 TRACE("BX = 0x%x\n", RegsOut
.w
.bx
);
99 TRACE("CX = 0x%x\n", RegsOut
.w
.cx
);
100 TRACE("DX = 0x%x\n", RegsOut
.w
.dx
);
101 TRACE("CF set = %s\n\n", (RegsOut
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
103 if (INT386_SUCCESS(RegsOut
))
105 // If AX=BX=0000h the use CX and DX
106 if (RegsOut
.w
.ax
== 0)
108 // Return extended memory size in K
109 *pMemoryAtSixteenMB
= RegsOut
.w
.dx
;
110 *pMemoryAtOneMB
= RegsOut
.w
.cx
;
115 // Return extended memory size in K
116 *pMemoryAtSixteenMB
= RegsOut
.w
.bx
;
117 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
122 // If we get here then Int15 Func E801h didn't work
123 // So try Int15 Func 88h
125 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
129 // CF clear if successful
130 // AX = number of contiguous KB starting at absolute address 100000h
133 // 80h invalid command (PC,PCjr)
134 // 86h unsupported function (XT,PS30)
136 Int386(0x15, &RegsIn
, &RegsOut
);
138 TRACE("Int15h AH=88h\n");
139 TRACE("AX = 0x%x\n", RegsOut
.w
.ax
);
140 TRACE("CF set = %s\n\n", (RegsOut
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
142 if (INT386_SUCCESS(RegsOut
) && RegsOut
.w
.ax
!= 0)
144 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
148 // If we get here then Int15 Func 88h didn't work
149 // So try reading the CMOS
150 WRITE_PORT_UCHAR((PUCHAR
)0x70, 0x31);
151 *pMemoryAtOneMB
= READ_PORT_UCHAR((PUCHAR
)0x71);
152 *pMemoryAtOneMB
= (*pMemoryAtOneMB
& 0xFFFF);
153 *pMemoryAtOneMB
= (*pMemoryAtOneMB
<< 8);
155 TRACE("Int15h Failed\n");
156 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB
);
158 if (*pMemoryAtOneMB
!= 0)
167 PcMemGetConventionalMemorySize(VOID
)
171 TRACE("GetConventionalMemorySize()\n");
174 * BIOS - GET MEMORY SIZE
177 * AX = kilobytes of contiguous memory starting at absolute address 00000h
179 * This call returns the contents of the word at 0040h:0013h;
180 * in PC and XT, this value is set from the switches on the motherboard
183 Int386(0x12, &Regs
, &Regs
);
186 TRACE("AX = 0x%x\n\n", Regs
.w
.ax
);
188 return (ULONG
)Regs
.w
.ax
;
193 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap
, ULONG MaxMemoryMapSize
)
197 ULONGLONG RealBaseAddress
, RealSize
;
198 TYPE_OF_MEMORY MemoryType
;
199 ASSERT(PcBiosMapCount
== 0);
201 TRACE("GetBiosMemoryMap()\n");
204 * Newer BIOSes - GET SYSTEM MEMORY MAP
208 * EDX = 534D4150h ('SMAP')
209 * EBX = continuation value or 00000000h to start at beginning of map
210 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
211 * ES:DI -> buffer for result
213 * CF clear if successful
214 * EAX = 534D4150h ('SMAP')
215 * ES:DI buffer filled
216 * EBX = next offset from which to copy or 00000000h if all done
217 * ECX = actual length returned in bytes
219 * AH = error code (86h)
221 Regs
.x
.ebx
= 0x00000000;
223 while (PcBiosMapCount
< MAX_BIOS_DESCRIPTORS
)
225 /* Setup the registers for the BIOS call */
226 Regs
.x
.eax
= 0x0000E820;
227 Regs
.x
.edx
= 0x534D4150; /* ('SMAP') */
228 /* Regs.x.ebx = 0x00000001; Continuation value already set */
229 Regs
.x
.ecx
= sizeof(BIOS_MEMORY_MAP
);
230 Regs
.w
.es
= BIOSCALLBUFSEGMENT
;
231 Regs
.w
.di
= BIOSCALLBUFOFFSET
;
232 Int386(0x15, &Regs
, &Regs
);
234 TRACE("Memory Map Entry %d\n", PcBiosMapCount
);
235 TRACE("Int15h AX=E820h\n");
236 TRACE("EAX = 0x%x\n", Regs
.x
.eax
);
237 TRACE("EBX = 0x%x\n", Regs
.x
.ebx
);
238 TRACE("ECX = 0x%x\n", Regs
.x
.ecx
);
239 TRACE("CF set = %s\n", (Regs
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
241 /* If the BIOS didn't return 'SMAP' in EAX then
242 * it doesn't support this call. If CF is set, we're done */
243 if (Regs
.x
.eax
!= 0x534D4150 || !INT386_SUCCESS(Regs
))
248 /* Copy data to global buffer */
249 RtlCopyMemory(&PcBiosMemoryMap
[PcBiosMapCount
], (PVOID
)BIOSCALLBUFFER
, Regs
.x
.ecx
);
251 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
);
252 TRACE("Length: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].Length
);
253 TRACE("Type: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Type
);
254 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Reserved
);
257 /* Check if this is free memory */
258 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryUsable
)
260 MemoryType
= LoaderFree
;
262 /* Align up base of memory area */
263 RealBaseAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
& ~(MM_PAGE_SIZE
- 1ULL);
265 /* Calculate the length after aligning the base */
266 RealSize
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
267 PcBiosMemoryMap
[PcBiosMapCount
].Length
- RealBaseAddress
;
268 RealSize
= (RealSize
+ MM_PAGE_SIZE
- 1) & ~(MM_PAGE_SIZE
- 1ULL);
272 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryReserved
)
273 MemoryType
= LoaderFirmwarePermanent
;
275 MemoryType
= LoaderSpecialMemory
;
277 /* Align down base of memory area */
278 RealBaseAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
& ~(MM_PAGE_SIZE
- 1ULL);
279 /* Calculate the length after aligning the base */
280 RealSize
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
281 PcBiosMemoryMap
[PcBiosMapCount
].Length
- RealBaseAddress
;
282 RealSize
= (RealSize
+ MM_PAGE_SIZE
- 1) & ~(MM_PAGE_SIZE
- 1ULL);
285 /* Check if we can add this descriptor */
286 if ((RealSize
>= MM_PAGE_SIZE
) && (MapCount
< MaxMemoryMapSize
))
288 /* Add the descriptor */
289 MapCount
= AddMemoryDescriptor(PcMemoryMap
,
290 MAX_BIOS_DESCRIPTORS
,
291 RealBaseAddress
/ MM_PAGE_SIZE
,
292 RealSize
/ MM_PAGE_SIZE
,
298 /* If the continuation value is zero or the
299 * carry flag is set then this was
300 * the last entry so we're done */
301 if (Regs
.x
.ebx
== 0x00000000)
303 TRACE("End Of System Memory Map!\n\n");
313 PFREELDR_MEMORY_DESCRIPTOR
314 PcMemGetMemoryMap(ULONG
*MemoryMapSize
)
317 ULONG ExtendedMemorySizeAtOneMB
;
318 ULONG ExtendedMemorySizeAtSixteenMB
;
320 EntryCount
= PcMemGetBiosMemoryMap(PcMemoryMap
, MAX_BIOS_DESCRIPTORS
);
322 /* If the BIOS didn't provide a memory map, synthesize one */
325 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB
, &ExtendedMemorySizeAtSixteenMB
);
327 /* Conventional memory */
328 AddMemoryDescriptor(PcMemoryMap
,
329 MAX_BIOS_DESCRIPTORS
,
331 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE
,
334 /* Extended memory */
335 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
336 MAX_BIOS_DESCRIPTORS
,
337 1024 * 1024 / PAGE_SIZE
,
338 ExtendedMemorySizeAtOneMB
* 1024 / PAGE_SIZE
,
341 if (ExtendedMemorySizeAtSixteenMB
!= 0)
343 /* Extended memory at 16MB */
344 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
345 MAX_BIOS_DESCRIPTORS
,
346 0x1000000 / PAGE_SIZE
,
347 ExtendedMemorySizeAtSixteenMB
* 64 * 1024 / PAGE_SIZE
,
352 TRACE("Dumping resulting memory map:\n");
353 for (i
= 0; i
< EntryCount
; i
++)
355 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
356 PcMemoryMap
[i
].BasePage
,
357 PcMemoryMap
[i
].PageCount
,
358 MmGetSystemMemoryMapTypeString(PcMemoryMap
[i
].MemoryType
));
361 *MemoryMapSize
= EntryCount
;