|
@@ -1,112 +1,83 @@
|
1
|
|
-#ifdef PCBIOS
|
2
|
|
-
|
|
1
|
+#include "stdint.h"
|
|
2
|
+#include "stddef.h"
|
|
3
|
+#include "memsizes.h"
|
3
|
4
|
#include "etherboot.h"
|
4
|
|
-#include "realmode.h" /* for real_mode_stack */
|
|
5
|
+#include "basemem.h"
|
5
|
6
|
|
6
|
7
|
/* Routines to allocate base memory in a BIOS-compatible way, by
|
7
|
8
|
* updating the Free Base Memory Size counter at 40:13h.
|
8
|
9
|
*
|
9
|
10
|
* Michael Brown <mbrown@fensystems.co.uk> (mcb30)
|
10
|
|
- * $Id$
|
|
11
|
+ *
|
|
12
|
+ * We no longer have anything to do with the real-mode stack. The
|
|
13
|
+ * only code that can end up creating a huge bubble of wasted base
|
|
14
|
+ * memory is the UNDI driver, so we make it the responsibility of the
|
|
15
|
+ * UNDI driver to reallocate the real-mode stack if required.
|
11
|
16
|
*/
|
12
|
17
|
|
13
|
|
-#define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) )
|
14
|
|
-#define BASE_MEMORY_MAX ( 640 )
|
15
|
|
-#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
|
16
|
|
-#define FREE_BASE_MEMORY ( (uint32_t) ( *fbms << 10 ) )
|
17
|
|
-
|
18
|
|
-/* Prototypes */
|
19
|
|
-void * _allot_base_memory ( size_t size );
|
20
|
|
-void _forget_base_memory ( void *ptr, size_t size );
|
21
|
|
-
|
22
|
|
-typedef struct free_base_memory_block {
|
23
|
|
- uint32_t magic;
|
24
|
|
- uint16_t size_kb;
|
25
|
|
-} free_base_memory_block_t;
|
26
|
|
-
|
27
|
|
-/* Return amount of free base memory in bytes
|
|
18
|
+/* "fbms" is an alias to the BIOS FBMS counter at 40:13, and acts just
|
|
19
|
+ * like any other uint16_t. We can't be used under -DKEEP_IT_REAL
|
|
20
|
+ * anyway, so we may as well be efficient.
|
28
|
21
|
*/
|
|
22
|
+#define fbms ( * ( ( uint16_t * ) phys_to_virt ( 0x413 ) ) )
|
|
23
|
+#define FBMS_MAX ( 640 )
|
29
|
24
|
|
30
|
|
-uint32_t get_free_base_memory ( void ) {
|
31
|
|
- return FREE_BASE_MEMORY;
|
32
|
|
-}
|
33
|
|
-
|
34
|
|
-/* Start of our image in base memory.
|
|
25
|
+/* Structure that we use to represent a free block of base memory
|
35
|
26
|
*/
|
36
|
|
-#define __text16_nocompress __attribute__ ((section (".text16.nocompress")))
|
37
|
|
-uint32_t image_basemem __text16_nocompress = 0;
|
38
|
|
-uint32_t image_basemem_size __text16_nocompress = 0;
|
|
27
|
+#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
|
|
28
|
+union free_base_memory_block {
|
|
29
|
+ struct {
|
|
30
|
+ uint32_t magic;
|
|
31
|
+ uint16_t size_kb;
|
|
32
|
+ };
|
|
33
|
+ char bytes[1024];
|
|
34
|
+};
|
|
35
|
+
|
|
36
|
+/* Local prototypes */
|
|
37
|
+static void free_unused_base_memory ( void );
|
|
38
|
+
|
|
39
|
+#undef DBG
|
|
40
|
+#ifdef DEBUG_BASEMEM
|
|
41
|
+#define DBG(...) printf ( __VA_ARGS__ )
|
|
42
|
+#else
|
|
43
|
+#define DBG(...)
|
|
44
|
+#endif
|
39
|
45
|
|
40
|
|
-/* Allot/free the real-mode stack
|
|
46
|
+/*
|
|
47
|
+ * Return amount of free base memory in bytes
|
|
48
|
+ *
|
41
|
49
|
*/
|
42
|
|
-
|
43
|
|
-void allot_real_mode_stack ( void )
|
44
|
|
-{
|
45
|
|
- void *new_real_mode_stack;
|
46
|
|
-
|
47
|
|
- if ( lock_real_mode_stack )
|
48
|
|
- return;
|
49
|
|
-
|
50
|
|
- /* This is evil hack.
|
51
|
|
- * Until we have a real_mode stack use 0x7c00.
|
52
|
|
- * Except for 0 - 0x600 membory below 0x7c00 is hardly every used.
|
53
|
|
- * This stack should never be used unless the stack allocation fails,
|
54
|
|
- * or if someone has placed a print statement in a dangerous location.
|
55
|
|
- */
|
56
|
|
- if (!real_mode_stack) {
|
57
|
|
- real_mode_stack = 0x7c00;
|
58
|
|
- }
|
59
|
|
- new_real_mode_stack = _allot_base_memory ( real_mode_stack_size );
|
60
|
|
- if ( ! new_real_mode_stack ) {
|
61
|
|
- printf ( "FATAL: No real-mode stack\n" );
|
62
|
|
- while ( 1 ) {};
|
63
|
|
- }
|
64
|
|
- real_mode_stack = virt_to_phys ( new_real_mode_stack );
|
65
|
|
- get_memsizes();
|
66
|
|
-}
|
67
|
|
-
|
68
|
|
-void forget_real_mode_stack ( void )
|
69
|
|
-{
|
70
|
|
- if ( lock_real_mode_stack )
|
71
|
|
- return;
|
72
|
|
-
|
73
|
|
- if ( real_mode_stack) {
|
74
|
|
- _forget_base_memory ( phys_to_virt(real_mode_stack),
|
75
|
|
- real_mode_stack_size );
|
76
|
|
- /* get_memsizes() uses the real_mode stack we just freed
|
77
|
|
- * for it's BIOS calls.
|
78
|
|
- */
|
79
|
|
- get_memsizes();
|
80
|
|
- real_mode_stack = 0;
|
81
|
|
- }
|
|
50
|
+uint32_t get_free_base_memory ( void ) {
|
|
51
|
+ return fbms << 10;
|
82
|
52
|
}
|
83
|
53
|
|
84
|
54
|
/* Allocate N bytes of base memory. Amount allocated will be rounded
|
85
|
55
|
* up to the nearest kB, since that's the granularity of the BIOS FBMS
|
86
|
56
|
* counter. Returns NULL if memory cannot be allocated.
|
|
57
|
+ *
|
87
|
58
|
*/
|
88
|
|
-
|
89
|
|
-static void * _allot_base_memory ( size_t size )
|
90
|
|
-{
|
|
59
|
+void * alloc_base_memory ( size_t size ) {
|
91
|
60
|
uint16_t size_kb = ( size + 1023 ) >> 10;
|
92
|
|
- void *ptr = NULL;
|
|
61
|
+ void *ptr;
|
93
|
62
|
|
94
|
|
-#ifdef DEBUG_BASEMEM
|
95
|
|
- printf ( "Trying to allocate %d kB of base memory from %d kB free\n",
|
96
|
|
- size_kb, *fbms );
|
97
|
|
-#endif
|
|
63
|
+ DBG ( "Trying to allocate %d bytes of base memory from %d kB free\n",
|
|
64
|
+ size, fbms );
|
98
|
65
|
|
99
|
66
|
/* Free up any unused memory before we start */
|
100
|
67
|
free_unused_base_memory();
|
101
|
68
|
|
102
|
69
|
/* Check available base memory */
|
103
|
|
- if ( size_kb > *fbms ) { return NULL; }
|
|
70
|
+ if ( size_kb > fbms ) {
|
|
71
|
+ DBG ( "Could not allocate %d kB of base memory: "
|
|
72
|
+ "only %d kB free\n", size_kb, fbms );
|
|
73
|
+ return NULL;
|
|
74
|
+ }
|
104
|
75
|
|
105
|
76
|
/* Reduce available base memory */
|
106
|
|
- *fbms -= size_kb;
|
|
77
|
+ fbms -= size_kb;
|
107
|
78
|
|
108
|
79
|
/* Calculate address of memory allocated */
|
109
|
|
- ptr = phys_to_virt ( FREE_BASE_MEMORY );
|
|
80
|
+ ptr = phys_to_virt ( fbms << 10 );
|
110
|
81
|
|
111
|
82
|
/* Zero out memory. We do this so that allocation of
|
112
|
83
|
* already-used space will show up in the form of a crash as
|
|
@@ -119,63 +90,43 @@ static void * _allot_base_memory ( size_t size )
|
119
|
90
|
*/
|
120
|
91
|
memset ( ptr, 0, size_kb << 10 );
|
121
|
92
|
|
122
|
|
-#ifdef DEBUG_BASEMEM
|
123
|
|
- printf ( "Allocated %d kB at [%x,%x)\n", size_kb,
|
124
|
|
- virt_to_phys ( ptr ),
|
125
|
|
- virt_to_phys ( ptr ) + size_kb * 1024 );
|
126
|
|
-#endif
|
127
|
|
-
|
128
|
|
- return ptr;
|
129
|
|
-}
|
|
93
|
+ DBG ( "Allocated %d kB of base memory at [%hx:0000,%hx:0000)\n",
|
|
94
|
+ size_kb, ( fbms << 6 ), ( ( fbms + size_kb ) << 6 ) );
|
130
|
95
|
|
131
|
|
-void * allot_base_memory ( size_t size )
|
132
|
|
-{
|
133
|
|
- void *ptr;
|
134
|
|
-
|
135
|
|
- /* Free real-mode stack, allocate memory, reallocate real-mode
|
136
|
|
- * stack.
|
137
|
|
- */
|
138
|
|
- forget_real_mode_stack();
|
139
|
|
- ptr = _allot_base_memory ( size );
|
|
96
|
+ /* Update our memory map */
|
140
|
97
|
get_memsizes();
|
|
98
|
+
|
141
|
99
|
return ptr;
|
142
|
100
|
}
|
143
|
101
|
|
144
|
|
-/* Free base memory allocated by allot_base_memory. The BIOS provides
|
|
102
|
+/* Free base memory allocated by alloc_base_memory. The BIOS provides
|
145
|
103
|
* nothing better than a LIFO mechanism for freeing memory (i.e. it
|
146
|
104
|
* just has the single "total free memory" counter), but we improve
|
147
|
|
- * upon this slightly; as long as you free all the allotted blocks, it
|
|
105
|
+ * upon this slightly; as long as you free all the allocated blocks, it
|
148
|
106
|
* doesn't matter what order you free them in. (This will only work
|
149
|
|
- * for blocks that are freed via forget_base_memory()).
|
|
107
|
+ * for blocks that are freed via free_base_memory()).
|
150
|
108
|
*
|
151
|
109
|
* Yes, it's annoying that you have to remember the size of the blocks
|
152
|
|
- * you've allotted. However, since our granularity of allocation is
|
|
110
|
+ * you've allocated. However, since our granularity of allocation is
|
153
|
111
|
* 1K, the alternative is to risk wasting the occasional kB of base
|
154
|
112
|
* memory, which is a Bad Thing. Really, you should be using as
|
155
|
113
|
* little base memory as possible, so consider the awkwardness of the
|
156
|
114
|
* API to be a feature! :-)
|
|
115
|
+ *
|
157
|
116
|
*/
|
158
|
|
-
|
159
|
|
-static void _forget_base_memory ( void *ptr, size_t size )
|
160
|
|
-{
|
161
|
|
- uint16_t remainder = virt_to_phys(ptr) & 1023;
|
|
117
|
+void free_base_memory ( void *ptr, size_t size ) {
|
|
118
|
+ uint16_t remainder = virt_to_phys ( ptr ) & 1023;
|
162
|
119
|
uint16_t size_kb = ( size + remainder + 1023 ) >> 10;
|
163
|
|
- free_base_memory_block_t *free_block =
|
164
|
|
- ( free_base_memory_block_t * ) ( ptr - remainder );
|
|
120
|
+ union free_base_memory_block *free_block =
|
|
121
|
+ ( ( void * ) ( ptr - remainder ) );
|
165
|
122
|
|
166
|
123
|
if ( ( ptr == NULL ) || ( size == 0 ) ) {
|
167
|
124
|
return;
|
168
|
125
|
}
|
169
|
126
|
|
170
|
|
-#ifdef DEBUG_BASEMEM
|
171
|
|
- printf ( "Trying to free %d bytes base memory at 0x%x\n",
|
172
|
|
- size, virt_to_phys ( ptr ) );
|
173
|
|
- if ( remainder > 0 ) {
|
174
|
|
- printf ( "WARNING: destructively expanding free block "
|
175
|
|
- "downwards to 0x%x\n",
|
176
|
|
- virt_to_phys ( ptr - remainder ) );
|
177
|
|
- }
|
178
|
|
-#endif
|
|
127
|
+ DBG ( "Trying to free %d bytes base memory at %hx:%hx\n", size,
|
|
128
|
+ ( virt_to_phys ( ptr - remainder ) >> 4 ),
|
|
129
|
+ ( virt_to_phys ( ptr - remainder ) & 0xf ) + remainder );
|
179
|
130
|
|
180
|
131
|
/* Mark every kilobyte within this block as free. This is
|
181
|
132
|
* overkill for normal purposes, but helps when something has
|
|
@@ -191,62 +142,51 @@ static void _forget_base_memory ( void *ptr, size_t size )
|
191
|
142
|
* keep this in so that debug messages are friendlier. It
|
192
|
143
|
* probably adds around 8 bytes to the overall code size.
|
193
|
144
|
*/
|
194
|
|
- while ( size_kb > 0 ) {
|
|
145
|
+ for ( ; size_kb > 0 ; free_block++, size_kb-- ) {
|
195
|
146
|
/* Mark this block as unused */
|
196
|
147
|
free_block->magic = FREE_BLOCK_MAGIC;
|
197
|
148
|
free_block->size_kb = size_kb;
|
198
|
|
- /* Move up by 1 kB */
|
199
|
|
- free_block = (void *)(((char *)free_block) + (1 << 10));
|
200
|
|
- size_kb--;
|
201
|
149
|
}
|
202
|
150
|
|
203
|
151
|
/* Free up unused base memory */
|
204
|
152
|
free_unused_base_memory();
|
205
|
|
-}
|
206
|
153
|
|
207
|
|
-void forget_base_memory ( void *ptr, size_t size )
|
208
|
|
-{
|
209
|
|
- /* Free memory, free real-mode stack, re-allocate real-mode
|
210
|
|
- * stack. Do this so that we don't end up wasting a huge
|
211
|
|
- * block of memory trapped behind the real-mode stack.
|
212
|
|
- */
|
213
|
|
- _forget_base_memory ( ptr, size );
|
214
|
|
- forget_real_mode_stack();
|
|
154
|
+ /* Update our memory map */
|
215
|
155
|
get_memsizes();
|
|
156
|
+
|
|
157
|
+ DBG ( "%d kB of base memory now free\n", fbms );
|
216
|
158
|
}
|
217
|
159
|
|
218
|
160
|
/* Do the actual freeing of memory. This is split out from
|
219
|
|
- * forget_base_memory() so that it may be called separately. It
|
|
161
|
+ * free_base_memory() so that it may be called separately. It
|
220
|
162
|
* should be called whenever base memory is deallocated by an external
|
221
|
163
|
* entity (if we can detect that it has done so) so that we get the
|
222
|
164
|
* chance to free up our own blocks.
|
223
|
165
|
*/
|
224
|
166
|
static void free_unused_base_memory ( void ) {
|
225
|
|
- free_base_memory_block_t *free_block = NULL;
|
|
167
|
+ union free_base_memory_block *free_block;
|
226
|
168
|
|
227
|
169
|
/* Try to release memory back to the BIOS. Free all
|
228
|
170
|
* consecutive blocks marked as free.
|
229
|
171
|
*/
|
230
|
172
|
while ( 1 ) {
|
231
|
173
|
/* Calculate address of next potential free block */
|
232
|
|
- free_block = ( free_base_memory_block_t * )
|
233
|
|
- phys_to_virt ( FREE_BASE_MEMORY );
|
|
174
|
+ free_block = phys_to_virt ( fbms << 10 );
|
234
|
175
|
|
235
|
176
|
/* Stop processing if we're all the way up to 640K or
|
236
|
177
|
* if this is not a free block
|
237
|
178
|
*/
|
238
|
|
- if ( ( *fbms == BASE_MEMORY_MAX ) ||
|
|
179
|
+ if ( ( fbms == FBMS_MAX ) ||
|
239
|
180
|
( free_block->magic != FREE_BLOCK_MAGIC ) ) {
|
240
|
181
|
break;
|
241
|
182
|
}
|
242
|
183
|
|
243
|
184
|
/* Return memory to BIOS */
|
244
|
|
- *fbms += free_block->size_kb;
|
|
185
|
+ fbms += free_block->size_kb;
|
245
|
186
|
|
246
|
|
-#ifdef DEBUG_BASEMEM
|
247
|
|
- printf ( "Freed %d kB base memory, %d kB now free\n",
|
248
|
|
- free_block->size_kb, *fbms );
|
249
|
|
-#endif
|
|
187
|
+ DBG ( "Freed %d kB of base memory at [%hx:0000,%hx:0000)\n",
|
|
188
|
+ free_block->size_kb, ( fbms << 6 ),
|
|
189
|
+ ( fbms + free_block->size_kb ) << 6 );
|
250
|
190
|
|
251
|
191
|
/* Zero out freed block. We do this in case
|
252
|
192
|
* the block contained any structures that
|
|
@@ -254,64 +194,6 @@ static void free_unused_base_memory ( void ) {
|
254
|
194
|
* memory.
|
255
|
195
|
*/
|
256
|
196
|
memset ( free_block, 0, free_block->size_kb << 10 );
|
257
|
|
- }
|
258
|
|
-}
|
259
|
197
|
|
260
|
|
-/* Free base memory used by the prefix. Called once at start of
|
261
|
|
- * Etherboot by arch_main().
|
262
|
|
- */
|
263
|
|
-void forget_prefix_base_memory ( void )
|
264
|
|
-{
|
265
|
|
- /* runtime_start_kb is _text rounded down to a physical kB boundary */
|
266
|
|
- uint32_t runtime_start_kb = virt_to_phys(_text) & ~0x3ff;
|
267
|
|
- /* prefix_size_kb is the prefix size excluding any portion
|
268
|
|
- * that overlaps into the first kB used by the runtime image
|
269
|
|
- */
|
270
|
|
- uint32_t prefix_size_kb = runtime_start_kb - image_basemem;
|
271
|
|
-
|
272
|
|
-#ifdef DEBUG_BASEMEM
|
273
|
|
- printf ( "Attempting to free base memory used by prefix\n" );
|
274
|
|
-#endif
|
275
|
|
-
|
276
|
|
- /* If the decompressor is in allocated base memory
|
277
|
|
- * *and* the Etherboot text is in base
|
278
|
|
- * memory, then free the decompressor.
|
279
|
|
- */
|
280
|
|
- if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
|
281
|
|
- ( runtime_start_kb >= FREE_BASE_MEMORY ) &&
|
282
|
|
- ( runtime_start_kb <= ( BASE_MEMORY_MAX << 10 ) ) )
|
283
|
|
- {
|
284
|
|
- forget_base_memory ( phys_to_virt ( image_basemem ),
|
285
|
|
- prefix_size_kb );
|
286
|
|
- /* Update image_basemem and image_basemem_size to
|
287
|
|
- * indicate that our allocation now starts with _text
|
288
|
|
- */
|
289
|
|
- image_basemem = runtime_start_kb;
|
290
|
|
- image_basemem_size -= prefix_size_kb;
|
291
|
198
|
}
|
292
|
199
|
}
|
293
|
|
-
|
294
|
|
-/* Free base memory used by the runtime image. Called after
|
295
|
|
- * relocation by arch_relocated_from().
|
296
|
|
- */
|
297
|
|
-void forget_runtime_base_memory ( unsigned long old_addr )
|
298
|
|
-{
|
299
|
|
- /* text_start_kb is old _text rounded down to a physical KB boundary */
|
300
|
|
- uint32_t old_text_start_kb = old_addr & ~0x3ff;
|
301
|
|
-
|
302
|
|
-#ifdef DEBUG_BASEMEM
|
303
|
|
- printf ( "Attempting to free base memory used by runtime image\n" );
|
304
|
|
-#endif
|
305
|
|
-
|
306
|
|
- if ( ( image_basemem >= FREE_BASE_MEMORY ) &&
|
307
|
|
- ( image_basemem == old_text_start_kb ) )
|
308
|
|
- {
|
309
|
|
- forget_base_memory ( phys_to_virt ( image_basemem ),
|
310
|
|
- image_basemem_size );
|
311
|
|
- /* Update image_basemem to show no longer in use */
|
312
|
|
- image_basemem = 0;
|
313
|
|
- image_basemem_size = 0;
|
314
|
|
- }
|
315
|
|
-}
|
316
|
|
-
|
317
|
|
-#endif /* PCBIOS */
|