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 DISKBUF_BASE_PAGE (DISKREADBUFFER / PAGE_SIZE)
33 #define STACK_BASE_PAGE (STACKLOWLIMIT / PAGE_SIZE)
34 #define STACK_END_PAGE (STACK32ADDR / PAGE_SIZE)
35 #define BIOSBUF_BASE_PAGE (BIOSCALLBUFFER / PAGE_SIZE)
37 #define FREELDR_PAGE_COUNT (DISKBUF_BASE_PAGE - FREELDR_BASE_PAGE)
38 #define DISKBUF_PAGE_COUNT (STACK_BASE_PAGE - DISKBUF_BASE_PAGE)
39 #define STACK_PAGE_COUNT (STACK_END_PAGE - STACK_BASE_PAGE)
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, FREELDR_BASE_PAGE
- 1 }, // freeldr stack + cmdline
49 { LoaderLoadedProgram
, FREELDR_BASE_PAGE
, FREELDR_PAGE_COUNT
}, // freeldr image
50 { LoaderFirmwareTemporary
, DISKBUF_BASE_PAGE
, DISKBUF_PAGE_COUNT
}, // Disk read buffer for int 13h. DISKREADBUFFER
51 { LoaderOsloaderStack
, STACK_BASE_PAGE
, STACK_PAGE_COUNT
}, // prot mode stack.
52 { LoaderFirmwareTemporary
, BIOSBUF_BASE_PAGE
, BIOSBUF_PAGE_COUNT
}, // BIOSCALLBUFFER
53 { LoaderFirmwarePermanent
, 0xA0, 0x50 }, // ROM / Video
54 { LoaderSpecialMemory
, 0xF0, 0x10 }, // ROM / Video
55 { LoaderSpecialMemory
, 0xFFF, 1 }, // unusable memory
56 { 0, 0, 0 }, // end of map
61 IN OUT PFREELDR_MEMORY_DESCRIPTOR List
,
63 IN PFN_NUMBER BasePage
,
64 IN PFN_NUMBER PageCount
,
65 IN TYPE_OF_MEMORY MemoryType
);
69 GetExtendedMemoryConfiguration(ULONG
* pMemoryAtOneMB
/* in KB */, ULONG
* pMemoryAtSixteenMB
/* in 64KB */)
74 TRACE("GetExtendedMemoryConfiguration()\n");
77 *pMemoryAtSixteenMB
= 0;
80 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
84 // CF clear if successful
85 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
86 // BX = extended memory above 16M, in 64K blocks
87 // CX = configured memory 1M to 16M, in K
88 // DX = configured memory above 16M, in 64K blocks
91 Int386(0x15, &RegsIn
, &RegsOut
);
93 TRACE("Int15h AX=E801h\n");
94 TRACE("AX = 0x%x\n", RegsOut
.w
.ax
);
95 TRACE("BX = 0x%x\n", RegsOut
.w
.bx
);
96 TRACE("CX = 0x%x\n", RegsOut
.w
.cx
);
97 TRACE("DX = 0x%x\n", RegsOut
.w
.dx
);
98 TRACE("CF set = %s\n\n", (RegsOut
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
100 if (INT386_SUCCESS(RegsOut
))
102 // If AX=BX=0000h the use CX and DX
103 if (RegsOut
.w
.ax
== 0)
105 // Return extended memory size in K
106 *pMemoryAtSixteenMB
= RegsOut
.w
.dx
;
107 *pMemoryAtOneMB
= RegsOut
.w
.cx
;
112 // Return extended memory size in K
113 *pMemoryAtSixteenMB
= RegsOut
.w
.bx
;
114 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
119 // If we get here then Int15 Func E801h didn't work
120 // So try Int15 Func 88h
122 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
126 // CF clear if successful
127 // AX = number of contiguous KB starting at absolute address 100000h
130 // 80h invalid command (PC,PCjr)
131 // 86h unsupported function (XT,PS30)
133 Int386(0x15, &RegsIn
, &RegsOut
);
135 TRACE("Int15h AH=88h\n");
136 TRACE("AX = 0x%x\n", RegsOut
.w
.ax
);
137 TRACE("CF set = %s\n\n", (RegsOut
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
139 if (INT386_SUCCESS(RegsOut
) && RegsOut
.w
.ax
!= 0)
141 *pMemoryAtOneMB
= RegsOut
.w
.ax
;
145 // If we get here then Int15 Func 88h didn't work
146 // So try reading the CMOS
147 WRITE_PORT_UCHAR((PUCHAR
)0x70, 0x31);
148 *pMemoryAtOneMB
= READ_PORT_UCHAR((PUCHAR
)0x71);
149 *pMemoryAtOneMB
= (*pMemoryAtOneMB
& 0xFFFF);
150 *pMemoryAtOneMB
= (*pMemoryAtOneMB
<< 8);
152 TRACE("Int15h Failed\n");
153 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB
);
155 if (*pMemoryAtOneMB
!= 0)
164 PcMemGetConventionalMemorySize(VOID
)
168 TRACE("GetConventionalMemorySize()\n");
171 * BIOS - GET MEMORY SIZE
174 * AX = kilobytes of contiguous memory starting at absolute address 00000h
176 * This call returns the contents of the word at 0040h:0013h;
177 * in PC and XT, this value is set from the switches on the motherboard
180 Int386(0x12, &Regs
, &Regs
);
183 TRACE("AX = 0x%x\n\n", Regs
.w
.ax
);
185 return (ULONG
)Regs
.w
.ax
;
190 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap
, ULONG MaxMemoryMapSize
)
194 ULONGLONG RealBaseAddress
, RealSize
;
195 TYPE_OF_MEMORY MemoryType
;
196 ASSERT(PcBiosMapCount
== 0);
198 TRACE("GetBiosMemoryMap()\n");
201 * Newer BIOSes - GET SYSTEM MEMORY MAP
205 * EDX = 534D4150h ('SMAP')
206 * EBX = continuation value or 00000000h to start at beginning of map
207 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
208 * ES:DI -> buffer for result
210 * CF clear if successful
211 * EAX = 534D4150h ('SMAP')
212 * ES:DI buffer filled
213 * EBX = next offset from which to copy or 00000000h if all done
214 * ECX = actual length returned in bytes
216 * AH = error code (86h)
218 Regs
.x
.ebx
= 0x00000000;
220 while (PcBiosMapCount
< MAX_BIOS_DESCRIPTORS
)
222 /* Setup the registers for the BIOS call */
223 Regs
.x
.eax
= 0x0000E820;
224 Regs
.x
.edx
= 0x534D4150; /* ('SMAP') */
225 /* Regs.x.ebx = 0x00000001; Continuation value already set */
226 Regs
.x
.ecx
= sizeof(BIOS_MEMORY_MAP
);
227 Regs
.w
.es
= BIOSCALLBUFSEGMENT
;
228 Regs
.w
.di
= BIOSCALLBUFOFFSET
;
229 Int386(0x15, &Regs
, &Regs
);
231 TRACE("Memory Map Entry %d\n", PcBiosMapCount
);
232 TRACE("Int15h AX=E820h\n");
233 TRACE("EAX = 0x%x\n", Regs
.x
.eax
);
234 TRACE("EBX = 0x%x\n", Regs
.x
.ebx
);
235 TRACE("ECX = 0x%x\n", Regs
.x
.ecx
);
236 TRACE("CF set = %s\n", (Regs
.x
.eflags
& EFLAGS_CF
) ? "TRUE" : "FALSE");
238 /* If the BIOS didn't return 'SMAP' in EAX then
239 * it doesn't support this call. If CF is set, we're done */
240 if (Regs
.x
.eax
!= 0x534D4150 || !INT386_SUCCESS(Regs
))
245 /* Copy data to global buffer */
246 RtlCopyMemory(&PcBiosMemoryMap
[PcBiosMapCount
], (PVOID
)BIOSCALLBUFFER
, Regs
.x
.ecx
);
248 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
);
249 TRACE("Length: 0x%llx\n", PcBiosMemoryMap
[PcBiosMapCount
].Length
);
250 TRACE("Type: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Type
);
251 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap
[PcBiosMapCount
].Reserved
);
254 /* Check if this is free memory */
255 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryUsable
)
257 MemoryType
= LoaderFree
;
259 /* Align up base of memory area */
260 RealBaseAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
& ~(MM_PAGE_SIZE
- 1ULL);
262 /* Calculate the length after aligning the base */
263 RealSize
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
264 PcBiosMemoryMap
[PcBiosMapCount
].Length
- RealBaseAddress
;
265 RealSize
= (RealSize
+ MM_PAGE_SIZE
- 1) & ~(MM_PAGE_SIZE
- 1ULL);
269 if (PcBiosMemoryMap
[PcBiosMapCount
].Type
== BiosMemoryReserved
)
270 MemoryType
= LoaderFirmwarePermanent
;
272 MemoryType
= LoaderSpecialMemory
;
274 /* Align down base of memory area */
275 RealBaseAddress
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
& ~(MM_PAGE_SIZE
- 1ULL);
276 /* Calculate the length after aligning the base */
277 RealSize
= PcBiosMemoryMap
[PcBiosMapCount
].BaseAddress
+
278 PcBiosMemoryMap
[PcBiosMapCount
].Length
- RealBaseAddress
;
279 RealSize
= (RealSize
+ MM_PAGE_SIZE
- 1) & ~(MM_PAGE_SIZE
- 1ULL);
282 /* Check if we can add this descriptor */
283 if ((RealSize
>= MM_PAGE_SIZE
) && (MapCount
< MaxMemoryMapSize
))
285 /* Add the descriptor */
286 MapCount
= AddMemoryDescriptor(PcMemoryMap
,
287 MAX_BIOS_DESCRIPTORS
,
288 (PFN_NUMBER
)(RealBaseAddress
/ MM_PAGE_SIZE
),
289 (PFN_NUMBER
)(RealSize
/ MM_PAGE_SIZE
),
295 /* If the continuation value is zero or the
296 * carry flag is set then this was
297 * the last entry so we're done */
298 if (Regs
.x
.ebx
== 0x00000000)
300 TRACE("End Of System Memory Map!\n\n");
310 PFREELDR_MEMORY_DESCRIPTOR
311 PcMemGetMemoryMap(ULONG
*MemoryMapSize
)
314 ULONG ExtendedMemorySizeAtOneMB
;
315 ULONG ExtendedMemorySizeAtSixteenMB
;
317 EntryCount
= PcMemGetBiosMemoryMap(PcMemoryMap
, MAX_BIOS_DESCRIPTORS
);
319 /* If the BIOS didn't provide a memory map, synthesize one */
322 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB
, &ExtendedMemorySizeAtSixteenMB
);
324 /* Conventional memory */
325 AddMemoryDescriptor(PcMemoryMap
,
326 MAX_BIOS_DESCRIPTORS
,
328 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE
,
331 /* Extended memory */
332 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
333 MAX_BIOS_DESCRIPTORS
,
334 1024 * 1024 / PAGE_SIZE
,
335 ExtendedMemorySizeAtOneMB
* 1024 / PAGE_SIZE
,
338 if (ExtendedMemorySizeAtSixteenMB
!= 0)
340 /* Extended memory at 16MB */
341 EntryCount
= AddMemoryDescriptor(PcMemoryMap
,
342 MAX_BIOS_DESCRIPTORS
,
343 0x1000000 / PAGE_SIZE
,
344 ExtendedMemorySizeAtSixteenMB
* 64 * 1024 / PAGE_SIZE
,
349 TRACE("Dumping resulting memory map:\n");
350 for (i
= 0; i
< EntryCount
; i
++)
352 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
353 PcMemoryMap
[i
].BasePage
,
354 PcMemoryMap
[i
].PageCount
,
355 MmGetSystemMemoryMapTypeString(PcMemoryMap
[i
].MemoryType
));
358 *MemoryMapSize
= EntryCount
;