Fix merge r65567.
[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 32
31
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
36 #define STACK_PAGE_COUNT (FREELDR_BASE_PAGE - STACK_BASE_PAGE)
37 #define FREELDR_PAGE_COUNT (DISKBUF_BASE_PAGE - FREELDR_BASE_PAGE)
38 #define DISKBUF_PAGE_COUNT (0x10)
39 #define BIOSBUF_PAGE_COUNT (1)
40
41 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
42 ULONG PcBiosMapCount;
43
44 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1] =
45 {
46 { LoaderFirmwarePermanent, 0x00, 1 }, // realmode int vectors
47 { LoaderFirmwareTemporary, 0x01, STACK_BASE_PAGE - 1 }, // freeldr stack, cmdline, BIOS call buffer
48 { LoaderOsloaderStack, STACK_BASE_PAGE, FREELDR_BASE_PAGE - STACK_BASE_PAGE }, // prot mode stack.
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 { LoaderFirmwarePermanent, 0x9F, 0x1 }, // EBDA
52 { LoaderFirmwarePermanent, 0xA0, 0x50 }, // ROM / Video
53 { LoaderSpecialMemory, 0xF0, 0x10 }, // ROM / Video
54 { LoaderSpecialMemory, 0xFFF, 1 }, // unusable memory
55 { 0, 0, 0 }, // end of map
56 };
57
58 ULONG
59 AddMemoryDescriptor(
60 IN OUT PFREELDR_MEMORY_DESCRIPTOR List,
61 IN ULONG MaxCount,
62 IN PFN_NUMBER BasePage,
63 IN PFN_NUMBER PageCount,
64 IN TYPE_OF_MEMORY MemoryType);
65
66 static
67 BOOLEAN
68 GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemoryAtSixteenMB /* in 64KB */)
69 {
70 REGS RegsIn;
71 REGS RegsOut;
72
73 TRACE("GetExtendedMemoryConfiguration()\n");
74
75 *pMemoryAtOneMB = 0;
76 *pMemoryAtSixteenMB = 0;
77
78 // Int 15h AX=E801h
79 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
80 //
81 // AX = E801h
82 // Return:
83 // CF clear if successful
84 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
85 // BX = extended memory above 16M, in 64K blocks
86 // CX = configured memory 1M to 16M, in K
87 // DX = configured memory above 16M, in 64K blocks
88 // CF set on error
89 RegsIn.w.ax = 0xE801;
90 Int386(0x15, &RegsIn, &RegsOut);
91
92 TRACE("Int15h AX=E801h\n");
93 TRACE("AX = 0x%x\n", RegsOut.w.ax);
94 TRACE("BX = 0x%x\n", RegsOut.w.bx);
95 TRACE("CX = 0x%x\n", RegsOut.w.cx);
96 TRACE("DX = 0x%x\n", RegsOut.w.dx);
97 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
98
99 if (INT386_SUCCESS(RegsOut))
100 {
101 // If AX=BX=0000h the use CX and DX
102 if (RegsOut.w.ax == 0)
103 {
104 // Return extended memory size in K
105 *pMemoryAtSixteenMB = RegsOut.w.dx;
106 *pMemoryAtOneMB = RegsOut.w.cx;
107 return TRUE;
108 }
109 else
110 {
111 // Return extended memory size in K
112 *pMemoryAtSixteenMB = RegsOut.w.bx;
113 *pMemoryAtOneMB = RegsOut.w.ax;
114 return TRUE;
115 }
116 }
117
118 // If we get here then Int15 Func E801h didn't work
119 // So try Int15 Func 88h
120 // Int 15h AH=88h
121 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
122 //
123 // AH = 88h
124 // Return:
125 // CF clear if successful
126 // AX = number of contiguous KB starting at absolute address 100000h
127 // CF set on error
128 // AH = status
129 // 80h invalid command (PC,PCjr)
130 // 86h unsupported function (XT,PS30)
131 RegsIn.b.ah = 0x88;
132 Int386(0x15, &RegsIn, &RegsOut);
133
134 TRACE("Int15h AH=88h\n");
135 TRACE("AX = 0x%x\n", RegsOut.w.ax);
136 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
137
138 if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
139 {
140 *pMemoryAtOneMB = RegsOut.w.ax;
141 return TRUE;
142 }
143
144 // If we get here then Int15 Func 88h didn't work
145 // So try reading the CMOS
146 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x31);
147 *pMemoryAtOneMB = READ_PORT_UCHAR((PUCHAR)0x71);
148 *pMemoryAtOneMB = (*pMemoryAtOneMB & 0xFFFF);
149 *pMemoryAtOneMB = (*pMemoryAtOneMB << 8);
150
151 TRACE("Int15h Failed\n");
152 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB);
153
154 if (*pMemoryAtOneMB != 0)
155 {
156 return TRUE;
157 }
158
159 return FALSE;
160 }
161
162 static ULONG
163 PcMemGetConventionalMemorySize(VOID)
164 {
165 REGS Regs;
166
167 TRACE("GetConventionalMemorySize()\n");
168
169 /* Int 12h
170 * BIOS - GET MEMORY SIZE
171 *
172 * Return:
173 * AX = kilobytes of contiguous memory starting at absolute address 00000h
174 *
175 * This call returns the contents of the word at 0040h:0013h;
176 * in PC and XT, this value is set from the switches on the motherboard
177 */
178 Regs.w.ax = 0;
179 Int386(0x12, &Regs, &Regs);
180
181 TRACE("Int12h\n");
182 TRACE("AX = 0x%x\n\n", Regs.w.ax);
183
184 return (ULONG)Regs.w.ax;
185 }
186
187 static
188 ULONG
189 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
190 {
191 REGS Regs;
192 ULONG MapCount = 0;
193 ULONGLONG RealBaseAddress, RealSize;
194 TYPE_OF_MEMORY MemoryType;
195 ULONG Size;
196 ASSERT(PcBiosMapCount == 0);
197
198 TRACE("GetBiosMemoryMap()\n");
199
200 /* Make sure the usable memory is large enough. To do this we check the 16
201 bit value at address 0x413 inside the BDA, which gives us the usable size
202 in KB */
203 Size = (*(PUSHORT)(ULONG_PTR)0x413) * 1024;
204 if (Size < MEMORY_MARGIN)
205 {
206 FrLdrBugCheckWithMessage(
207 MEMORY_INIT_FAILURE,
208 __FILE__,
209 __LINE__,
210 "The BIOS reported a usable memory range up to 0x%x, which is too small!\n\n"
211 "If you see this, please report to the ReactOS team!",
212 Size);
213 }
214
215 /* Get the address of the Extended BIOS Data Area (EBDA).
216 * Int 15h, AH=C1h
217 * SYSTEM - RETURN EXTENDED-BIOS DATA-AREA SEGMENT ADDRESS (PS)
218 *
219 * Return:
220 * CF set on error
221 * CF clear if successful
222 * ES = segment of data area
223 */
224 Regs.x.eax = 0x0000C100;
225 Int386(0x15, &Regs, &Regs);
226
227 /* If the function fails, there is no EBDA */
228 if (INT386_SUCCESS(Regs))
229 {
230 /* Check if this is high enough */
231 ULONG EbdaBase = (ULONG)Regs.w.es << 4;
232 if (EbdaBase < MEMORY_MARGIN)
233 {
234 FrLdrBugCheckWithMessage(
235 MEMORY_INIT_FAILURE,
236 __FILE__,
237 __LINE__,
238 "The location of your EBDA is 0x%lx, which is too low!\n\n"
239 "If you see this, please report to the ReactOS team!",
240 EbdaBase);
241 }
242
243 /* Calculate the (max) size of the EBDA */
244 Size = 0xA0000 - EbdaBase;
245
246 /* Add the descriptor */
247 MapCount = AddMemoryDescriptor(PcMemoryMap,
248 MAX_BIOS_DESCRIPTORS,
249 (EbdaBase / MM_PAGE_SIZE),
250 (Size / MM_PAGE_SIZE),
251 LoaderFirmwarePermanent);
252 }
253
254 /* Int 15h AX=E820h
255 * Newer BIOSes - GET SYSTEM MEMORY MAP
256 *
257 * AX = E820h
258 * EAX = 0000E820h
259 * EDX = 534D4150h ('SMAP')
260 * EBX = continuation value or 00000000h to start at beginning of map
261 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
262 * ES:DI -> buffer for result
263 * Return:
264 * CF clear if successful
265 * EAX = 534D4150h ('SMAP')
266 * ES:DI buffer filled
267 * EBX = next offset from which to copy or 00000000h if all done
268 * ECX = actual length returned in bytes
269 * CF set on error
270 * AH = error code (86h)
271 */
272 Regs.x.ebx = 0x00000000;
273
274 while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
275 {
276 /* Setup the registers for the BIOS call */
277 Regs.x.eax = 0x0000E820;
278 Regs.x.edx = 0x534D4150; /* ('SMAP') */
279 /* Regs.x.ebx = 0x00000001; Continuation value already set */
280 Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
281 Regs.w.es = BIOSCALLBUFSEGMENT;
282 Regs.w.di = BIOSCALLBUFOFFSET;
283 Int386(0x15, &Regs, &Regs);
284
285 TRACE("Memory Map Entry %d\n", PcBiosMapCount);
286 TRACE("Int15h AX=E820h\n");
287 TRACE("EAX = 0x%x\n", Regs.x.eax);
288 TRACE("EBX = 0x%x\n", Regs.x.ebx);
289 TRACE("ECX = 0x%x\n", Regs.x.ecx);
290 TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
291
292 /* If the BIOS didn't return 'SMAP' in EAX then
293 * it doesn't support this call. If CF is set, we're done */
294 if (Regs.x.eax != 0x534D4150 || !INT386_SUCCESS(Regs))
295 {
296 break;
297 }
298
299 /* Copy data to global buffer */
300 RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
301
302 TRACE("BaseAddress: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
303 TRACE("Length: 0x%llx\n", PcBiosMemoryMap[PcBiosMapCount].Length);
304 TRACE("Type: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Type);
305 TRACE("Reserved: 0x%lx\n", PcBiosMemoryMap[PcBiosMapCount].Reserved);
306 TRACE("\n");
307
308 /* Check if this is free memory */
309 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
310 {
311 MemoryType = LoaderFree;
312
313 /* Align up base of memory area */
314 RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
315
316 /* Calculate the length after aligning the base */
317 RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
318 PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
319 RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
320 }
321 else
322 {
323 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
324 MemoryType = LoaderFirmwarePermanent;
325 else
326 MemoryType = LoaderSpecialMemory;
327
328 /* Align down base of memory area */
329 RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
330 /* Calculate the length after aligning the base */
331 RealSize = PcBiosMemoryMap[PcBiosMapCount].BaseAddress +
332 PcBiosMemoryMap[PcBiosMapCount].Length - RealBaseAddress;
333 RealSize = (RealSize + MM_PAGE_SIZE - 1) & ~(MM_PAGE_SIZE - 1ULL);
334 }
335
336 /* Check if we can add this descriptor */
337 if ((RealSize >= MM_PAGE_SIZE) && (MapCount < MaxMemoryMapSize))
338 {
339 /* Add the descriptor */
340 MapCount = AddMemoryDescriptor(PcMemoryMap,
341 MAX_BIOS_DESCRIPTORS,
342 (PFN_NUMBER)(RealBaseAddress / MM_PAGE_SIZE),
343 (PFN_NUMBER)(RealSize / MM_PAGE_SIZE),
344 MemoryType);
345 }
346
347 PcBiosMapCount++;
348
349 /* If the continuation value is zero or the
350 * carry flag is set then this was
351 * the last entry so we're done */
352 if (Regs.x.ebx == 0x00000000)
353 {
354 TRACE("End Of System Memory Map!\n\n");
355 break;
356 }
357
358 }
359
360 return MapCount;
361 }
362
363
364 PFREELDR_MEMORY_DESCRIPTOR
365 PcMemGetMemoryMap(ULONG *MemoryMapSize)
366 {
367 ULONG i, EntryCount;
368 ULONG ExtendedMemorySizeAtOneMB;
369 ULONG ExtendedMemorySizeAtSixteenMB;
370
371 EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
372
373 /* If the BIOS didn't provide a memory map, synthesize one */
374 if (0 == EntryCount)
375 {
376 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
377
378 /* Conventional memory */
379 AddMemoryDescriptor(PcMemoryMap,
380 MAX_BIOS_DESCRIPTORS,
381 0,
382 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
383 LoaderFree);
384
385 /* Extended memory */
386 EntryCount = AddMemoryDescriptor(PcMemoryMap,
387 MAX_BIOS_DESCRIPTORS,
388 1024 * 1024 / PAGE_SIZE,
389 ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
390 LoaderFree);
391
392 if (ExtendedMemorySizeAtSixteenMB != 0)
393 {
394 /* Extended memory at 16MB */
395 EntryCount = AddMemoryDescriptor(PcMemoryMap,
396 MAX_BIOS_DESCRIPTORS,
397 0x1000000 / PAGE_SIZE,
398 ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
399 LoaderFree);
400 }
401 }
402
403 TRACE("Dumping resulting memory map:\n");
404 for (i = 0; i < EntryCount; i++)
405 {
406 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
407 PcMemoryMap[i].BasePage,
408 PcMemoryMap[i].PageCount,
409 MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
410 }
411
412 *MemoryMapSize = EntryCount;
413
414 return PcMemoryMap;
415 }
416
417 /* EOF */