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