Create a branch for working on csrss and co.
[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 #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)
36
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)
41
42 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
43 ULONG PcBiosMapCount;
44
45 FREELDR_MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1] =
46 {
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
57 };
58
59 ULONG
60 AddMemoryDescriptor(
61 IN OUT PFREELDR_MEMORY_DESCRIPTOR List,
62 IN ULONG MaxCount,
63 IN PFN_NUMBER BasePage,
64 IN PFN_NUMBER PageCount,
65 IN TYPE_OF_MEMORY MemoryType);
66
67 static
68 BOOLEAN
69 GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemoryAtSixteenMB /* in 64KB */)
70 {
71 REGS RegsIn;
72 REGS RegsOut;
73
74 TRACE("GetExtendedMemoryConfiguration()\n");
75
76 *pMemoryAtOneMB = 0;
77 *pMemoryAtSixteenMB = 0;
78
79 // Int 15h AX=E801h
80 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
81 //
82 // AX = E801h
83 // Return:
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
89 // CF set on error
90 RegsIn.w.ax = 0xE801;
91 Int386(0x15, &RegsIn, &RegsOut);
92
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");
99
100 if (INT386_SUCCESS(RegsOut))
101 {
102 // If AX=BX=0000h the use CX and DX
103 if (RegsOut.w.ax == 0)
104 {
105 // Return extended memory size in K
106 *pMemoryAtSixteenMB = RegsOut.w.dx;
107 *pMemoryAtOneMB = RegsOut.w.cx;
108 return TRUE;
109 }
110 else
111 {
112 // Return extended memory size in K
113 *pMemoryAtSixteenMB = RegsOut.w.bx;
114 *pMemoryAtOneMB = RegsOut.w.ax;
115 return TRUE;
116 }
117 }
118
119 // If we get here then Int15 Func E801h didn't work
120 // So try Int15 Func 88h
121 // Int 15h AH=88h
122 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
123 //
124 // AH = 88h
125 // Return:
126 // CF clear if successful
127 // AX = number of contiguous KB starting at absolute address 100000h
128 // CF set on error
129 // AH = status
130 // 80h invalid command (PC,PCjr)
131 // 86h unsupported function (XT,PS30)
132 RegsIn.b.ah = 0x88;
133 Int386(0x15, &RegsIn, &RegsOut);
134
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");
138
139 if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
140 {
141 *pMemoryAtOneMB = RegsOut.w.ax;
142 return TRUE;
143 }
144
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);
151
152 TRACE("Int15h Failed\n");
153 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB);
154
155 if (*pMemoryAtOneMB != 0)
156 {
157 return TRUE;
158 }
159
160 return FALSE;
161 }
162
163 static ULONG
164 PcMemGetConventionalMemorySize(VOID)
165 {
166 REGS Regs;
167
168 TRACE("GetConventionalMemorySize()\n");
169
170 /* Int 12h
171 * BIOS - GET MEMORY SIZE
172 *
173 * Return:
174 * AX = kilobytes of contiguous memory starting at absolute address 00000h
175 *
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
178 */
179 Regs.w.ax = 0;
180 Int386(0x12, &Regs, &Regs);
181
182 TRACE("Int12h\n");
183 TRACE("AX = 0x%x\n\n", Regs.w.ax);
184
185 return (ULONG)Regs.w.ax;
186 }
187
188 static
189 ULONG
190 PcMemGetBiosMemoryMap(PFREELDR_MEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
191 {
192 REGS Regs;
193 ULONG MapCount = 0;
194 ULONGLONG RealBaseAddress, RealSize;
195 TYPE_OF_MEMORY MemoryType;
196 ASSERT(PcBiosMapCount == 0);
197
198 TRACE("GetBiosMemoryMap()\n");
199
200 /* Int 15h AX=E820h
201 * Newer BIOSes - GET SYSTEM MEMORY MAP
202 *
203 * AX = E820h
204 * EAX = 0000E820h
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
209 * Return:
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
215 * CF set on error
216 * AH = error code (86h)
217 */
218 Regs.x.ebx = 0x00000000;
219
220 while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
221 {
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);
230
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");
237
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))
241 {
242 break;
243 }
244
245 /* Copy data to global buffer */
246 RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
247
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);
252 TRACE("\n");
253
254 /* Check if this is free memory */
255 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
256 {
257 MemoryType = LoaderFree;
258
259 /* Align up base of memory area */
260 RealBaseAddress = PcBiosMemoryMap[PcBiosMapCount].BaseAddress & ~(MM_PAGE_SIZE - 1ULL);
261
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);
266 }
267 else
268 {
269 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryReserved)
270 MemoryType = LoaderFirmwarePermanent;
271 else
272 MemoryType = LoaderSpecialMemory;
273
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);
280 }
281
282 /* Check if we can add this descriptor */
283 if ((RealSize >= MM_PAGE_SIZE) && (MapCount < MaxMemoryMapSize))
284 {
285 /* Add the descriptor */
286 MapCount = AddMemoryDescriptor(PcMemoryMap,
287 MAX_BIOS_DESCRIPTORS,
288 RealBaseAddress / MM_PAGE_SIZE,
289 RealSize / MM_PAGE_SIZE,
290 MemoryType);
291 }
292
293 PcBiosMapCount++;
294
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)
299 {
300 TRACE("End Of System Memory Map!\n\n");
301 break;
302 }
303
304 }
305
306 return MapCount;
307 }
308
309
310 PFREELDR_MEMORY_DESCRIPTOR
311 PcMemGetMemoryMap(ULONG *MemoryMapSize)
312 {
313 ULONG i, EntryCount;
314 ULONG ExtendedMemorySizeAtOneMB;
315 ULONG ExtendedMemorySizeAtSixteenMB;
316
317 EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
318
319 /* If the BIOS didn't provide a memory map, synthesize one */
320 if (0 == EntryCount)
321 {
322 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
323
324 /* Conventional memory */
325 AddMemoryDescriptor(PcMemoryMap,
326 MAX_BIOS_DESCRIPTORS,
327 0,
328 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
329 LoaderFree);
330
331 /* Extended memory */
332 EntryCount = AddMemoryDescriptor(PcMemoryMap,
333 MAX_BIOS_DESCRIPTORS,
334 1024 * 1024 / PAGE_SIZE,
335 ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
336 LoaderFree);
337
338 if (ExtendedMemorySizeAtSixteenMB != 0)
339 {
340 /* Extended memory at 16MB */
341 EntryCount = AddMemoryDescriptor(PcMemoryMap,
342 MAX_BIOS_DESCRIPTORS,
343 0x1000000 / PAGE_SIZE,
344 ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
345 LoaderFree);
346 }
347 }
348
349 TRACE("Dumping resulting memory map:\n");
350 for (i = 0; i < EntryCount; i++)
351 {
352 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
353 PcMemoryMap[i].BasePage,
354 PcMemoryMap[i].PageCount,
355 MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
356 }
357
358 *MemoryMapSize = EntryCount;
359
360 return PcMemoryMap;
361 }
362
363 /* EOF */