4cce53d7fa5a3a72df875a6f56ef346dd09d3392
[people/xl0/gpxe.git] / src / arch / i386 / firmware / pcbios / memsizes.c
1 #include "stdint.h"
2 #include "stddef.h"
3 #include "realmode.h"
4 #include <gpxe/init.h>
5 #include "etherboot.h"
6 #include "memsizes.h"
7
8 #define CF ( 1 << 0 )
9
10 /* by Eric Biederman */
11
12 struct meminfo meminfo;
13
14 /**************************************************************************
15 BASEMEMSIZE - Get size of the conventional (base) memory
16 **************************************************************************/
17 static unsigned short basememsize ( void ) {
18         uint16_t int12_basememsize, fbms_basememsize;
19         uint16_t basememsize;
20
21         /* There are two methods for retrieving the base memory size:
22          * INT 12 and the BIOS FBMS counter at 40:13.  We read both
23          * and use the smaller value, to be paranoid.
24          * 
25          * We then store the smaller value in the BIOS FBMS counter so
26          * that other code (e.g. basemem.c) can rely on it and not
27          * have to use INT 12.  This is especially important because
28          * basemem.c functions can be called in a context in which
29          * there is no real-mode stack (e.g. when trying to allocate
30          * memory for a real-mode stack...)
31          */
32
33         REAL_EXEC ( rm_basememsize,
34                     "int $0x12\n\t",
35                     1,
36                     OUT_CONSTRAINTS ( "=a" ( int12_basememsize ) ),
37                     IN_CONSTRAINTS (),
38                     CLOBBER ( "ebx", "ecx", "edx", "ebp", "esi", "edi" ) );
39
40         get_real ( fbms_basememsize, 0x40, 0x13 );
41
42         basememsize = ( int12_basememsize < fbms_basememsize ?
43                         int12_basememsize : fbms_basememsize );
44
45         put_real ( basememsize, 0x40, 0x13 );
46
47         return basememsize;
48 }
49
50 /**************************************************************************
51 MEMSIZE - Determine size of extended memory, in kB
52 **************************************************************************/
53 static unsigned int memsize ( void ) {
54         uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k;
55         uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k;
56         uint16_t flags;
57         int memsize;
58
59         /* Try INT 15,e801 first
60          *
61          * Some buggy BIOSes don't clear/set carry on pass/error of
62          * e801h memory size call or merely pass cx,dx through without
63          * changing them, so we set carry and zero cx,dx before call.
64          */
65         REAL_EXEC ( rm_mem_e801,
66                     "stc\n\t"
67                     "int $0x15\n\t"
68                     "pushfw\n\t"        /* flags -> %di */
69                     "popw %%di\n\t",
70                     5,
71                     OUT_CONSTRAINTS ( "=a" ( extmem_1m_to_16m_k ),
72                                       "=b" ( extmem_16m_plus_64k ),
73                                       "=c" ( confmem_1m_to_16m_k ),
74                                       "=d" ( confmem_16m_plus_64k ),
75                                       "=D" ( flags ) ),
76                     IN_CONSTRAINTS ( "a" ( 0xe801 ),
77                                      "c" ( 0 ),
78                                      "d" ( 0 ) ),
79                     CLOBBER ( "ebp", "esi" ) );
80
81         if ( ! ( flags & CF ) ) {
82                 /* INT 15,e801 succeeded */
83                 if ( confmem_1m_to_16m_k || confmem_16m_plus_64k ) {
84                         /* Use confmem (cx,dx) values */
85                         memsize = confmem_1m_to_16m_k +
86                                 ( confmem_16m_plus_64k << 6 );
87                 } else {
88                         /* Use extmem (ax,bx) values */
89                         memsize = extmem_1m_to_16m_k +
90                                 ( extmem_16m_plus_64k << 6 );
91                 }
92         } else {
93                 /* INT 15,e801 failed; fall back to INT 15,88
94                  *
95                  * CF is apparently unreliable and should be ignored.
96                  */
97                 REAL_EXEC ( rm_mem_88,
98                             "int $0x15\n\t",
99                             1,
100                             OUT_CONSTRAINTS ( "=a" ( extmem_1m_to_16m_k ) ),
101                             IN_CONSTRAINTS ( "a" ( 0x88 << 8 ) ),
102                             CLOBBER ( "ebx", "ecx", "edx",
103                                       "ebp", "esi", "edi" ) );
104                 memsize = extmem_1m_to_16m_k;
105         }
106
107         return memsize;
108 }
109
110 /**************************************************************************
111 MEME820 - Retrieve the E820 BIOS memory map
112 **************************************************************************/
113 #define SMAP ( 0x534d4150 ) /* "SMAP" */
114 static int meme820 ( struct e820entry *buf, int count ) {
115         int index;
116         uint16_t basemem_entry;
117         uint32_t smap, next;
118         uint16_t flags;
119         uint32_t discard_c, discard_d;
120
121         index = 0;
122         next = 0;
123         do {
124                 basemem_entry = BASEMEM_PARAMETER_INIT ( buf[index] );
125                 REAL_EXEC ( rm_mem_e820,
126                             "int $0x15\n\t"
127                             "pushfw\n\t"        /* flags -> %di */
128                             "popw %%di\n\t",
129                             5,
130                             OUT_CONSTRAINTS ( "=a" ( smap ),
131                                               "=b" ( next ),
132                                               "=c" ( discard_c ),
133                                               "=d" ( discard_d ),
134                                               "=D" ( flags ) ),
135                             IN_CONSTRAINTS ( "a" ( 0xe820 ),
136                                              "b" ( next ),
137                                              "c" ( sizeof (struct e820entry) ),
138                                              "d" ( SMAP ),
139                                              "D" ( basemem_entry ) ),
140                             CLOBBER ( "ebp", "esi" ) );
141                 BASEMEM_PARAMETER_DONE ( buf[index] );
142                 if ( smap != SMAP ) return 0;
143                 if ( flags & CF ) break;
144                 index++;
145         } while ( ( index < count ) && ( next != 0 ) );
146                 
147         return index;
148 }
149
150 /**************************************************************************
151 GET_MEMSIZES - Retrieve the system memory map via any available means
152 **************************************************************************/
153 void get_memsizes ( void ) {
154         /* Ensure we don't stomp bios data structutres.
155          * the interrupt table: 0x000 - 0x3ff
156          * the bios data area:  0x400 - 0x502
157          * Dos variables:       0x502 - 0x5ff
158          */
159         static const unsigned min_addr = 0x600;
160         unsigned i;
161         unsigned basemem;
162
163         /* Retrieve memory information from the BIOS */
164         meminfo.basememsize = basememsize();
165         basemem = meminfo.basememsize << 10;
166         meminfo.memsize = memsize();
167 #ifndef IGNORE_E820_MAP
168         meminfo.map_count = meme820 ( meminfo.map, E820MAX );
169 #else
170         meminfo.map_count = 0;
171 #endif
172
173         /* If we don't have an e820 memory map fake it */
174         if ( meminfo.map_count == 0 ) {
175                 meminfo.map_count = 2;
176                 meminfo.map[0].addr = 0;
177                 meminfo.map[0].size = meminfo.basememsize << 10;
178                 meminfo.map[0].type = E820_RAM;
179                 meminfo.map[1].addr = 1024*1024;
180                 meminfo.map[1].size = meminfo.memsize << 10;
181                 meminfo.map[1].type = E820_RAM;
182         }
183
184         /* Scrub the e820 map */
185         for ( i = 0; i < meminfo.map_count; i++ ) {
186                 if ( meminfo.map[i].type != E820_RAM ) {
187                         continue;
188                 }
189
190                 /* Reserve the bios data structures */
191                 if ( meminfo.map[i].addr < min_addr ) {
192                         unsigned long delta;
193                         delta = min_addr - meminfo.map[i].addr;
194                         if ( delta > meminfo.map[i].size ) {
195                                 delta = meminfo.map[i].size;
196                         }
197                         meminfo.map[i].addr = min_addr;
198                         meminfo.map[i].size -= delta;
199                 }
200
201                 /* Ensure the returned e820 map is in sync with the
202                  * actual memory state
203                  */
204                 if ( ( meminfo.map[i].addr < 0xa0000 ) && 
205                      (( meminfo.map[i].addr+meminfo.map[i].size ) > basemem )){
206                         if ( meminfo.map[i].addr <= basemem ) {
207                                 meminfo.map[i].size = basemem
208                                         - meminfo.map[i].addr;
209                         } else {
210                                 meminfo.map[i].addr = basemem;
211                                 meminfo.map[i].size = 0;
212                         }
213                 }
214         }
215
216 #ifdef DEBUG_MEMSIZES
217         printf ( "basememsize %d\n", meminfo.basememsize );
218         printf ( "memsize %d\n",     meminfo.memsize );
219         printf ( "Memory regions(%d):\n", meminfo.map_count );
220         for ( i = 0; i < meminfo.map_count; i++ ) {
221                 unsigned long long r_start, r_end;
222                 r_start = meminfo.map[i].addr;
223                 r_end = r_start + meminfo.map[i].size;
224                 printf ( "[%X%X, %X%X) type %d\n", 
225                          ( unsigned long ) ( r_start >> 32 ),
226                          ( unsigned long ) r_start,
227                          ( unsigned long ) ( r_end >> 32 ),
228                          ( unsigned long ) r_end,
229                          meminfo.map[i].type );
230         }
231 #endif
232
233 }
234
235 INIT_FN ( INIT_MEMSIZES, get_memsizes, NULL, NULL );