568c54a11e5e80f89aeed8abb707441d9233873f
[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
25 #define NDEBUG
26 #include <debug.h>
27
28 DBG_DEFAULT_CHANNEL(MEMORY);
29
30 #define MAX_BIOS_DESCRIPTORS 32
31
32 BIOS_MEMORY_MAP PcBiosMemoryMap[MAX_BIOS_DESCRIPTORS];
33 ULONG PcBiosMapCount;
34
35 static
36 BOOLEAN
37 GetExtendedMemoryConfiguration(ULONG* pMemoryAtOneMB /* in KB */, ULONG* pMemoryAtSixteenMB /* in 64KB */)
38 {
39 REGS RegsIn;
40 REGS RegsOut;
41
42 TRACE("GetExtendedMemoryConfiguration()\n");
43
44 *pMemoryAtOneMB = 0;
45 *pMemoryAtSixteenMB = 0;
46
47 // Int 15h AX=E801h
48 // Phoenix BIOS v4.0 - GET MEMORY SIZE FOR >64M CONFIGURATIONS
49 //
50 // AX = E801h
51 // Return:
52 // CF clear if successful
53 // AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
54 // BX = extended memory above 16M, in 64K blocks
55 // CX = configured memory 1M to 16M, in K
56 // DX = configured memory above 16M, in 64K blocks
57 // CF set on error
58 RegsIn.w.ax = 0xE801;
59 Int386(0x15, &RegsIn, &RegsOut);
60
61 TRACE("Int15h AX=E801h\n");
62 TRACE("AX = 0x%x\n", RegsOut.w.ax);
63 TRACE("BX = 0x%x\n", RegsOut.w.bx);
64 TRACE("CX = 0x%x\n", RegsOut.w.cx);
65 TRACE("DX = 0x%x\n", RegsOut.w.dx);
66 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
67
68 if (INT386_SUCCESS(RegsOut))
69 {
70 // If AX=BX=0000h the use CX and DX
71 if (RegsOut.w.ax == 0)
72 {
73 // Return extended memory size in K
74 *pMemoryAtSixteenMB = RegsOut.w.dx;
75 *pMemoryAtOneMB = RegsOut.w.cx;
76 return TRUE;
77 }
78 else
79 {
80 // Return extended memory size in K
81 *pMemoryAtSixteenMB = RegsOut.w.bx;
82 *pMemoryAtOneMB = RegsOut.w.ax;
83 return TRUE;
84 }
85 }
86
87 // If we get here then Int15 Func E801h didn't work
88 // So try Int15 Func 88h
89 // Int 15h AH=88h
90 // SYSTEM - GET EXTENDED MEMORY SIZE (286+)
91 //
92 // AH = 88h
93 // Return:
94 // CF clear if successful
95 // AX = number of contiguous KB starting at absolute address 100000h
96 // CF set on error
97 // AH = status
98 // 80h invalid command (PC,PCjr)
99 // 86h unsupported function (XT,PS30)
100 RegsIn.b.ah = 0x88;
101 Int386(0x15, &RegsIn, &RegsOut);
102
103 TRACE("Int15h AH=88h\n");
104 TRACE("AX = 0x%x\n", RegsOut.w.ax);
105 TRACE("CF set = %s\n\n", (RegsOut.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
106
107 if (INT386_SUCCESS(RegsOut) && RegsOut.w.ax != 0)
108 {
109 *pMemoryAtOneMB = RegsOut.w.ax;
110 return TRUE;
111 }
112
113 // If we get here then Int15 Func 88h didn't work
114 // So try reading the CMOS
115 WRITE_PORT_UCHAR((PUCHAR)0x70, 0x31);
116 *pMemoryAtOneMB = READ_PORT_UCHAR((PUCHAR)0x71);
117 *pMemoryAtOneMB = (*pMemoryAtOneMB & 0xFFFF);
118 *pMemoryAtOneMB = (*pMemoryAtOneMB << 8);
119
120 TRACE("Int15h Failed\n");
121 TRACE("CMOS reports: 0x%x\n", *pMemoryAtOneMB);
122
123 if (*pMemoryAtOneMB != 0)
124 {
125 return TRUE;
126 }
127
128 return FALSE;
129 }
130
131 static ULONG
132 PcMemGetConventionalMemorySize(VOID)
133 {
134 REGS Regs;
135
136 TRACE("GetConventionalMemorySize()\n");
137
138 /* Int 12h
139 * BIOS - GET MEMORY SIZE
140 *
141 * Return:
142 * AX = kilobytes of contiguous memory starting at absolute address 00000h
143 *
144 * This call returns the contents of the word at 0040h:0013h;
145 * in PC and XT, this value is set from the switches on the motherboard
146 */
147 Regs.w.ax = 0;
148 Int386(0x12, &Regs, &Regs);
149
150 TRACE("Int12h\n");
151 TRACE("AX = 0x%x\n\n", Regs.w.ax);
152
153 return (ULONG)Regs.w.ax;
154 }
155
156 static ULONG
157 PcMemGetBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MaxMemoryMapSize)
158 {
159 REGS Regs;
160 ULONG MapCount;
161
162 TRACE("GetBiosMemoryMap()\n");
163
164 /* Int 15h AX=E820h
165 * Newer BIOSes - GET SYSTEM MEMORY MAP
166 *
167 * AX = E820h
168 * EAX = 0000E820h
169 * EDX = 534D4150h ('SMAP')
170 * EBX = continuation value or 00000000h to start at beginning of map
171 * ECX = size of buffer for result, in bytes (should be >= 20 bytes)
172 * ES:DI -> buffer for result
173 * Return:
174 * CF clear if successful
175 * EAX = 534D4150h ('SMAP')
176 * ES:DI buffer filled
177 * EBX = next offset from which to copy or 00000000h if all done
178 * ECX = actual length returned in bytes
179 * CF set on error
180 * AH = error code (86h)
181 */
182 Regs.x.ebx = 0x00000000;
183
184 for (MapCount = 0; MapCount < MaxMemoryMapSize; MapCount++)
185 {
186 /* Setup the registers for the BIOS call */
187 Regs.x.eax = 0x0000E820;
188 Regs.x.edx = 0x534D4150; /* ('SMAP') */
189 /* Regs.x.ebx = 0x00000001; Continuation value already set */
190 Regs.x.ecx = sizeof(BIOS_MEMORY_MAP);
191 Regs.w.es = BIOSCALLBUFSEGMENT;
192 Regs.w.di = BIOSCALLBUFOFFSET;
193 Int386(0x15, &Regs, &Regs);
194
195 TRACE("Memory Map Entry %d\n", MapCount);
196 TRACE("Int15h AX=E820h\n");
197 TRACE("EAX = 0x%x\n", Regs.x.eax);
198 TRACE("EBX = 0x%x\n", Regs.x.ebx);
199 TRACE("ECX = 0x%x\n", Regs.x.ecx);
200 TRACE("CF set = %s\n", (Regs.x.eflags & EFLAGS_CF) ? "TRUE" : "FALSE");
201
202 /* If the BIOS didn't return 'SMAP' in EAX then
203 * it doesn't support this call. If CF is set, we're done */
204 if (Regs.x.eax != 0x534D4150 || !INT386_SUCCESS(Regs))
205 {
206 break;
207 }
208
209 /* Copy data to caller's buffer */
210 RtlCopyMemory(&BiosMemoryMap[MapCount], (PVOID)BIOSCALLBUFFER, Regs.x.ecx);
211
212 TRACE("BaseAddress: 0x%p\n", (PVOID)(ULONG_PTR)BiosMemoryMap[MapCount].BaseAddress);
213 TRACE("Length: 0x%p\n", (PVOID)(ULONG_PTR)BiosMemoryMap[MapCount].Length);
214 TRACE("Type: 0x%x\n", BiosMemoryMap[MapCount].Type);
215 TRACE("Reserved: 0x%x\n", BiosMemoryMap[MapCount].Reserved);
216 TRACE("\n");
217
218 /* If the continuation value is zero or the
219 * carry flag is set then this was
220 * the last entry so we're done */
221 if (Regs.x.ebx == 0x00000000)
222 {
223 TRACE("End Of System Memory Map!\n\n");
224 break;
225 }
226
227 }
228
229 return MapCount;
230 }
231
232 PBIOS_MEMORY_MAP
233 PcMemGetMemoryMap(ULONG *MemoryMapSize)
234 {
235 ULONG EntryCount;
236 ULONG ExtendedMemorySizeAtOneMB;
237 ULONG ExtendedMemorySizeAtSixteenMB;
238
239 EntryCount = PcMemGetBiosMemoryMap(PcBiosMemoryMap, MAX_BIOS_DESCRIPTORS);
240 PcBiosMapCount = EntryCount;
241
242 /* If the BIOS didn't provide a memory map, synthesize one */
243 if (0 == EntryCount)
244 {
245 GetExtendedMemoryConfiguration(&ExtendedMemorySizeAtOneMB, &ExtendedMemorySizeAtSixteenMB);
246
247 /* Conventional memory */
248 PcBiosMemoryMap[EntryCount].BaseAddress = 0;
249 PcBiosMemoryMap[EntryCount].Length = PcMemGetConventionalMemorySize() * 1024;
250 PcBiosMemoryMap[EntryCount].Type = BiosMemoryUsable;
251 EntryCount++;
252
253 /* Extended memory at 1MB */
254 PcBiosMemoryMap[EntryCount].BaseAddress = 1024 * 1024;
255 PcBiosMemoryMap[EntryCount].Length = ExtendedMemorySizeAtOneMB * 1024;
256 PcBiosMemoryMap[EntryCount].Type = BiosMemoryUsable;
257 EntryCount++;
258
259 if (ExtendedMemorySizeAtSixteenMB != 0)
260 {
261 /* Extended memory at 16MB */
262 PcBiosMemoryMap[EntryCount].BaseAddress = 0x1000000;
263 PcBiosMemoryMap[EntryCount].Length = ExtendedMemorySizeAtSixteenMB * 64 * 1024;
264 PcBiosMemoryMap[EntryCount].Type = BiosMemoryUsable;
265 EntryCount++;
266 }
267 }
268
269 *MemoryMapSize = EntryCount;
270
271 return PcBiosMemoryMap;
272 }
273
274 /* EOF */