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