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