Browse Source

Rewritten in a much saner way, now that we don't have to worry about

continually reallocating the real-mode stack.
tags/v0.9.3
Michael Brown 19 years ago
parent
commit
48feb91a40
1 changed files with 78 additions and 196 deletions
  1. 78
    196
      src/arch/i386/firmware/pcbios/basemem.c

+ 78
- 196
src/arch/i386/firmware/pcbios/basemem.c View File

@@ -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 */

Loading…
Cancel
Save