[FREELDR]
[reactos.git] / reactos / boot / freeldr / freeldr / arch / i386 / pcmem.c
1 /* $Id$
2 *
3 * FreeLoader
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Note: Most of this code comes from the old file "i386mem.c", which
20 * was Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
21 */
22
23 #include <freeldr.h>
24 #include <arch/pc/x86common.h>
25
26 #define NDEBUG
27 #include <debug.h>
28
29 DBG_DEFAULT_CHANNEL(MEMORY);
30
31 #define MAX_BIOS_DESCRIPTORS 32
32 #define FREELDR_BASE_PAGE (FREELDR_BASE / PAGE_SIZE)
33 #define FILEBUF_BASE_PAGE (FILESYSBUFFER / PAGE_SIZE)
34 #define DISKBUF_BASE_PAGE (DISKREADBUFFER / PAGE_SIZE)
35 #define STACK_BASE_PAGE (DISKBUF_BASE_PAGE + 1)
36 #define STACK_END_PAGE (STACK32ADDR / PAGE_SIZE)
37 #define BIOSBUF_BASE_PAGE (BIOSCALLBUFFER / PAGE_SIZE)
38
39 #define FREELDR_PAGE_COUNT (FILEBUF_BASE_PAGE - FREELDR_BASE_PAGE)
40 #define FILEBUF_PAGE_COUNT (DISKBUF_BASE_PAGE - FILEBUF_BASE_PAGE)
41 #define DISKBUF_PAGE_COUNT (1)
42 #define STACK_PAGE_COUNT (STACK_END_PAGE - STACK_BASE_PAGE)
43 #define BIOSBUF_PAGE_COUNT (0xA0 - BIOSBUF_BASE_PAGE)
44
45 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
46 ULONG PcBiosMapCount;
47
48 MEMORY_DESCRIPTOR PcMemoryMap[MAX_BIOS_DESCRIPTORS + 1] =
49 {
50 { MemoryFirmwarePermanent, 0x00, 1 }, // realmode int vectors
51 { MemoryFirmwareTemporary, 0x01, FREELDR_BASE_PAGE - 1 }, // freeldr stack + cmdline
52 { MemoryLoadedProgram, FREELDR_BASE_PAGE, FREELDR_PAGE_COUNT }, // freeldr image
53 { MemoryFirmwareTemporary, FILEBUF_BASE_PAGE, FILEBUF_PAGE_COUNT }, // File system read buffer. FILESYSBUFFER
54 { MemoryFirmwareTemporary, DISKBUF_BASE_PAGE, DISKBUF_PAGE_COUNT }, // Disk read buffer for int 13h. DISKREADBUFFER
55 { MemorySpecialMemory, STACK_BASE_PAGE, STACK_PAGE_COUNT }, // prot mode stack.
56 { MemoryFirmwareTemporary, BIOSBUF_BASE_PAGE, BIOSBUF_PAGE_COUNT }, // BIOSCALLBUFFER
57 { MemoryFirmwarePermanent, 0xA0, 0x60 }, // ROM / Video
58 { MemorySpecialMemory, 0xFFF, 1 }, // unusable memory
59 { MemorySpecialMemory, MAXULONG_PTR, 0 }, // end of map
60 };
61
62 ULONG
63 AddMemoryDescriptor(
64 IN OUT PMEMORY_DESCRIPTOR List,
65 IN ULONG MaxCount,
66 IN PFN_NUMBER BasePage,
67 IN PFN_NUMBER PageCount,
68 IN MEMORY_TYPE 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(PMEMORY_DESCRIPTOR MemoryMap, ULONG MaxMemoryMapSize)
194 {
195 REGS Regs;
196 ULONG MapCount = 0;
197 ULONGLONG RealBaseAddress, RealSize;
198 ASSERT(PcBiosMapCount == 0);
199
200 TRACE("GetBiosMemoryMap()\n");
201
202 /* Int 15h AX=E820h
203 * Newer BIOSes - GET SYSTEM MEMORY MAP
204 *
205 * AX = E820h
206 * EAX = 0000E820h
207 * EDX = 534D4150h ('SMAP')
208 * EBX = continuation value or 00000000h to start at beginning of map
209 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
210 * ES:DI -> buffer for result
211 * Return:
212 * CF clear if successful
213 * EAX = 534D4150h ('SMAP')
214 * ES:DI buffer filled
215 * EBX = next offset from which to copy or 00000000h if all done
216 * ECX = actual length returned in bytes
217 * CF set on error
218 * AH = error code (86h)
219 */
220 Regs.x.ebx = 0x00000000;
221
222 while (PcBiosMapCount < MAX_BIOS_DESCRIPTORS)
223 {
224 /* Setup the registers for the BIOS call */
225 Regs.x.eax = 0x0000E820;
226 Regs.x.edx = 0x534D4150; /* ('SMAP') */
227 /* Regs.x.ebx = 0x00000001; Continuation value already set */
228 Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
229 Regs.w.es = BIOSCALLBUFSEGMENT;
230 Regs.w.di = BIOSCALLBUFOFFSET;
231 Int386(0x15, &Regs, &Regs);
232
233 TRACE("Memory Map Entry %d\n", PcBiosMapCount);
234 TRACE("Int15h AX=E820h\n");
235 TRACE("EAX = 0x%x\n", Regs.x.eax);
236 TRACE("EBX = 0x%x\n", Regs.x.ebx);
237 TRACE("ECX = 0x%x\n", Regs.x.ecx);
238 TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
239
240 /* If the BIOS didn't return 'SMAP' in EAX then
241 * it doesn't support this call. If CF is set, we're done */
242 if (Regs.x.eax != 0x534D4150 || !INT386_SUCCESS(Regs))
243 {
244 break;
245 }
246
247 /* Copy data to global buffer */
248 RtlCopyMemory(&PcBiosMemoryMap[PcBiosMapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
249
250 TRACE("BaseAddress: 0x%p\n", (PVOID)(ULONG_PTR)PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
251 TRACE("Length: 0x%p\n", (PVOID)(ULONG_PTR)PcBiosMemoryMap[PcBiosMapCount].Length);
252 TRACE("Type: 0x%x\n", PcBiosMemoryMap[PcBiosMapCount].Type);
253 TRACE("Reserved: 0x%x\n", PcBiosMemoryMap[PcBiosMapCount].Reserved);
254 TRACE("\n");
255
256 /* Align up base of memory area */
257 RealBaseAddress = ROUND_UP(PcBiosMemoryMap[PcBiosMapCount].BaseAddress, MM_PAGE_SIZE);
258 RealSize = PcBiosMemoryMap[PcBiosMapCount].Length -
259 (RealBaseAddress - PcBiosMemoryMap[PcBiosMapCount].BaseAddress);
260
261 /* Check if we can add this descriptor */
262 if ((RealSize >= MM_PAGE_SIZE) && (MapCount < MaxMemoryMapSize))
263 {
264 MEMORY_TYPE MemoryType;
265
266 if (PcBiosMemoryMap[PcBiosMapCount].Type == BiosMemoryUsable)
267 MemoryType = MemoryFree;
268 else
269 MemoryType = MemoryFirmwarePermanent;
270
271 /* Add the descriptor */
272 MapCount = AddMemoryDescriptor(PcMemoryMap,
273 MAX_BIOS_DESCRIPTORS,
274 RealBaseAddress / MM_PAGE_SIZE,
275 RealSize / MM_PAGE_SIZE,
276 MemoryType);
277 }
278
279 PcBiosMapCount++;
280
281 /* If the continuation value is zero or the
282 * carry flag is set then this was
283 * the last entry so we're done */
284 if (Regs.x.ebx == 0x00000000)
285 {
286 TRACE("End Of System Memory Map!\n\n");
287 break;
288 }
289
290 }
291
292 return MapCount;
293 }
294
295
296 PMEMORY_DESCRIPTOR
297 PcMemGetMemoryMap(ULONG *MemoryMapSize)
298 {
299 ULONG i, EntryCount;
300 ULONG ExtendedMemorySizeAtOneMB;
301 ULONG ExtendedMemorySizeAtSixteenMB;
302
303 EntryCount = PcMemGetBiosMemoryMap(PcMemoryMap, MAX_BIOS_DESCRIPTORS);
304
305 /* If the BIOS didn't provide a memory map, synthesize one */
306 if (0 == EntryCount)
307 {
308 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
309
310 /* Conventional memory */
311 AddMemoryDescriptor(PcMemoryMap,
312 MAX_BIOS_DESCRIPTORS,
313 0,
314 PcMemGetConventionalMemorySize() * 1024 / PAGE_SIZE,
315 MemoryFree);
316
317 /* Extended memory */
318 EntryCount = AddMemoryDescriptor(PcMemoryMap,
319 MAX_BIOS_DESCRIPTORS,
320 1024 * 1024 / PAGE_SIZE,
321 ExtendedMemorySizeAtOneMB * 1024 / PAGE_SIZE,
322 MemoryFree);
323 EntryCount++;
324
325 if (ExtendedMemorySizeAtSixteenMB != 0)
326 {
327 /* Extended memory at 16MB */
328 EntryCount = AddMemoryDescriptor(PcMemoryMap,
329 MAX_BIOS_DESCRIPTORS,
330 0x1000000 / PAGE_SIZE,
331 ExtendedMemorySizeAtSixteenMB * 64 * 1024 / PAGE_SIZE,
332 MemoryFree);
333 }
334 }
335
336 TRACE("Dumping resulting memory map:\n");
337 for (i = 0; i < EntryCount; i++)
338 {
339 TRACE("BasePage=0x%lx, PageCount=0x%lx, Type=%s\n",
340 PcMemoryMap[i].BasePage,
341 PcMemoryMap[i].PageCount,
342 MmGetSystemMemoryMapTypeString(PcMemoryMap[i].MemoryType));
343 }
344
345 *MemoryMapSize = EntryCount;
346
347 return PcMemoryMap;
348 }
349
350 /* EOF */