xref: /onnv-gate/usr/src/grub/grub-0.97/netboot/basemem.c (revision 8044:b3af80bbf173)
1*8044SWilliam.Kucharski@Sun.COM #include "etherboot.h"
2*8044SWilliam.Kucharski@Sun.COM #define DEBUG_BASEMEM
3*8044SWilliam.Kucharski@Sun.COM /* Routines to allocate base memory in a BIOS-compatible way, by
4*8044SWilliam.Kucharski@Sun.COM  * updating the Free Base Memory Size counter at 40:13h.
5*8044SWilliam.Kucharski@Sun.COM  *
6*8044SWilliam.Kucharski@Sun.COM  * Michael Brown <mbrown@fensystems.co.uk> (mcb30)
7*8044SWilliam.Kucharski@Sun.COM  * $Id: basemem.c,v 1.5 2004/06/17 12:48:08 fengshuo Exp $
8*8044SWilliam.Kucharski@Sun.COM  */
9*8044SWilliam.Kucharski@Sun.COM 
10*8044SWilliam.Kucharski@Sun.COM #define fbms ( ( uint16_t * ) phys_to_virt ( 0x413 ) )
11*8044SWilliam.Kucharski@Sun.COM #define BASE_MEMORY_MAX ( 640 )
12*8044SWilliam.Kucharski@Sun.COM #define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) )
13*8044SWilliam.Kucharski@Sun.COM 
14*8044SWilliam.Kucharski@Sun.COM typedef struct free_base_memory_block {
15*8044SWilliam.Kucharski@Sun.COM 	uint32_t	magic;
16*8044SWilliam.Kucharski@Sun.COM 	uint16_t	size_kb;
17*8044SWilliam.Kucharski@Sun.COM } free_base_memory_block_t;
18*8044SWilliam.Kucharski@Sun.COM 
19*8044SWilliam.Kucharski@Sun.COM /* Return amount of free base memory in bytes
20*8044SWilliam.Kucharski@Sun.COM  */
21*8044SWilliam.Kucharski@Sun.COM 
get_free_base_memory(void)22*8044SWilliam.Kucharski@Sun.COM uint32_t get_free_base_memory ( void ) {
23*8044SWilliam.Kucharski@Sun.COM 	return *fbms << 10;
24*8044SWilliam.Kucharski@Sun.COM }
25*8044SWilliam.Kucharski@Sun.COM 
26*8044SWilliam.Kucharski@Sun.COM /* Adjust the real mode stack pointer.  We keep the real mode stack at
27*8044SWilliam.Kucharski@Sun.COM  * the top of free base memory, rather than allocating space for it.
28*8044SWilliam.Kucharski@Sun.COM  */
29*8044SWilliam.Kucharski@Sun.COM 
adjust_real_mode_stack(void)30*8044SWilliam.Kucharski@Sun.COM static inline void adjust_real_mode_stack ( void ) {
31*8044SWilliam.Kucharski@Sun.COM /*  	real_mode_stack = ( *fbms << 10 ); */
32*8044SWilliam.Kucharski@Sun.COM }
33*8044SWilliam.Kucharski@Sun.COM 
34*8044SWilliam.Kucharski@Sun.COM /* Allocate N bytes of base memory.  Amount allocated will be rounded
35*8044SWilliam.Kucharski@Sun.COM  * up to the nearest kB, since that's the granularity of the BIOS FBMS
36*8044SWilliam.Kucharski@Sun.COM  * counter.  Returns NULL if memory cannot be allocated.
37*8044SWilliam.Kucharski@Sun.COM  */
38*8044SWilliam.Kucharski@Sun.COM 
allot_base_memory(size_t size)39*8044SWilliam.Kucharski@Sun.COM void * allot_base_memory ( size_t size ) {
40*8044SWilliam.Kucharski@Sun.COM 	uint16_t size_kb = ( size + 1023 ) >> 10;
41*8044SWilliam.Kucharski@Sun.COM 	void *ptr = NULL;
42*8044SWilliam.Kucharski@Sun.COM 
43*8044SWilliam.Kucharski@Sun.COM #ifdef DEBUG_BASEMEM
44*8044SWilliam.Kucharski@Sun.COM 	printf ( "Trying to allocate %d kB of base memory, %d kB free\n",
45*8044SWilliam.Kucharski@Sun.COM 		 size_kb, *fbms );
46*8044SWilliam.Kucharski@Sun.COM #endif
47*8044SWilliam.Kucharski@Sun.COM 
48*8044SWilliam.Kucharski@Sun.COM 	/* Free up any unused memory before we start */
49*8044SWilliam.Kucharski@Sun.COM 	free_unused_base_memory();
50*8044SWilliam.Kucharski@Sun.COM 
51*8044SWilliam.Kucharski@Sun.COM 	/* Check available base memory */
52*8044SWilliam.Kucharski@Sun.COM 	if ( size_kb > *fbms ) { return NULL; }
53*8044SWilliam.Kucharski@Sun.COM 
54*8044SWilliam.Kucharski@Sun.COM 	/* Reduce available base memory */
55*8044SWilliam.Kucharski@Sun.COM 	*fbms -= size_kb;
56*8044SWilliam.Kucharski@Sun.COM 
57*8044SWilliam.Kucharski@Sun.COM 	/* Calculate address of memory allocated */
58*8044SWilliam.Kucharski@Sun.COM 	ptr = phys_to_virt ( *fbms << 10 );
59*8044SWilliam.Kucharski@Sun.COM 
60*8044SWilliam.Kucharski@Sun.COM #ifdef DEBUG_BASEMEM
61*8044SWilliam.Kucharski@Sun.COM 	/* Zero out memory.  We do this so that allocation of
62*8044SWilliam.Kucharski@Sun.COM 	 * already-used space will show up in the form of a crash as
63*8044SWilliam.Kucharski@Sun.COM 	 * soon as possible.
64*8044SWilliam.Kucharski@Sun.COM 	 */
65*8044SWilliam.Kucharski@Sun.COM 	memset ( ptr, 0, size_kb << 10 );
66*8044SWilliam.Kucharski@Sun.COM #endif
67*8044SWilliam.Kucharski@Sun.COM 
68*8044SWilliam.Kucharski@Sun.COM 	/* Adjust real mode stack pointer */
69*8044SWilliam.Kucharski@Sun.COM 	adjust_real_mode_stack ();
70*8044SWilliam.Kucharski@Sun.COM 
71*8044SWilliam.Kucharski@Sun.COM 	return ptr;
72*8044SWilliam.Kucharski@Sun.COM }
73*8044SWilliam.Kucharski@Sun.COM 
74*8044SWilliam.Kucharski@Sun.COM /* Free base memory allocated by allot_base_memory.  The BIOS provides
75*8044SWilliam.Kucharski@Sun.COM  * nothing better than a LIFO mechanism for freeing memory (i.e. it
76*8044SWilliam.Kucharski@Sun.COM  * just has the single "total free memory" counter), but we improve
77*8044SWilliam.Kucharski@Sun.COM  * upon this slightly; as long as you free all the allotted blocks, it
78*8044SWilliam.Kucharski@Sun.COM  * doesn't matter what order you free them in.  (This will only work
79*8044SWilliam.Kucharski@Sun.COM  * for blocks that are freed via forget_base_memory()).
80*8044SWilliam.Kucharski@Sun.COM  *
81*8044SWilliam.Kucharski@Sun.COM  * Yes, it's annoying that you have to remember the size of the blocks
82*8044SWilliam.Kucharski@Sun.COM  * you've allotted.  However, since our granularity of allocation is
83*8044SWilliam.Kucharski@Sun.COM  * 1K, the alternative is to risk wasting the occasional kB of base
84*8044SWilliam.Kucharski@Sun.COM  * memory, which is a Bad Thing.  Really, you should be using as
85*8044SWilliam.Kucharski@Sun.COM  * little base memory as possible, so consider the awkwardness of the
86*8044SWilliam.Kucharski@Sun.COM  * API to be a feature! :-)
87*8044SWilliam.Kucharski@Sun.COM  */
88*8044SWilliam.Kucharski@Sun.COM 
forget_base_memory(void * ptr,size_t size)89*8044SWilliam.Kucharski@Sun.COM void forget_base_memory ( void *ptr, size_t size ) {
90*8044SWilliam.Kucharski@Sun.COM 	uint16_t remainder = virt_to_phys(ptr) & 1023;
91*8044SWilliam.Kucharski@Sun.COM 	uint16_t size_kb = ( size + remainder + 1023 ) >> 10;
92*8044SWilliam.Kucharski@Sun.COM 	free_base_memory_block_t *free_block =
93*8044SWilliam.Kucharski@Sun.COM 		( free_base_memory_block_t * ) ( ptr - remainder );
94*8044SWilliam.Kucharski@Sun.COM 
95*8044SWilliam.Kucharski@Sun.COM 	if ( ( ptr == NULL ) || ( size == 0 ) ) { return; }
96*8044SWilliam.Kucharski@Sun.COM 
97*8044SWilliam.Kucharski@Sun.COM #ifdef DEBUG_BASEMEM
98*8044SWilliam.Kucharski@Sun.COM 	printf ( "Trying to free %d bytes base memory at 0x%x\n",
99*8044SWilliam.Kucharski@Sun.COM 		 size, virt_to_phys ( ptr ) );
100*8044SWilliam.Kucharski@Sun.COM 	if ( remainder > 0 ) {
101*8044SWilliam.Kucharski@Sun.COM 		printf ( "WARNING: destructively expanding free block "
102*8044SWilliam.Kucharski@Sun.COM 			 "downwards to 0x%x\n",
103*8044SWilliam.Kucharski@Sun.COM 			 virt_to_phys ( ptr - remainder ) );
104*8044SWilliam.Kucharski@Sun.COM 	}
105*8044SWilliam.Kucharski@Sun.COM #endif
106*8044SWilliam.Kucharski@Sun.COM 
107*8044SWilliam.Kucharski@Sun.COM 	/* Mark every kilobyte within this block as free.  This is
108*8044SWilliam.Kucharski@Sun.COM 	 * overkill for normal purposes, but helps when something has
109*8044SWilliam.Kucharski@Sun.COM 	 * allocated base memory with a granularity finer than the
110*8044SWilliam.Kucharski@Sun.COM 	 * BIOS granularity of 1kB.  PXE ROMs tend to do this when
111*8044SWilliam.Kucharski@Sun.COM 	 * they allocate their own memory.  This method allows us to
112*8044SWilliam.Kucharski@Sun.COM 	 * free their blocks (admittedly in a rather dangerous,
113*8044SWilliam.Kucharski@Sun.COM 	 * tread-on-anything-either-side sort of way, but there's no
114*8044SWilliam.Kucharski@Sun.COM 	 * other way to do it).
115*8044SWilliam.Kucharski@Sun.COM 	 *
116*8044SWilliam.Kucharski@Sun.COM 	 * Since we're marking every kB as free, there's actually no
117*8044SWilliam.Kucharski@Sun.COM 	 * need for recording the size of the blocks.  However, we
118*8044SWilliam.Kucharski@Sun.COM 	 * keep this in so that debug messages are friendlier.  It
119*8044SWilliam.Kucharski@Sun.COM 	 * probably adds around 8 bytes to the overall code size.
120*8044SWilliam.Kucharski@Sun.COM 	 */
121*8044SWilliam.Kucharski@Sun.COM 	while ( size_kb > 0 ) {
122*8044SWilliam.Kucharski@Sun.COM 		/* Mark this block as unused */
123*8044SWilliam.Kucharski@Sun.COM 		free_block->magic = FREE_BLOCK_MAGIC;
124*8044SWilliam.Kucharski@Sun.COM 		free_block->size_kb = size_kb;
125*8044SWilliam.Kucharski@Sun.COM 		/* Move up by 1 kB */
126*8044SWilliam.Kucharski@Sun.COM 		free_block = (void *)free_block + ( 1 << 10 );
127*8044SWilliam.Kucharski@Sun.COM 		size_kb--;
128*8044SWilliam.Kucharski@Sun.COM 	}
129*8044SWilliam.Kucharski@Sun.COM 
130*8044SWilliam.Kucharski@Sun.COM 	/* Free up unused base memory */
131*8044SWilliam.Kucharski@Sun.COM 	free_unused_base_memory();
132*8044SWilliam.Kucharski@Sun.COM }
133*8044SWilliam.Kucharski@Sun.COM 
134*8044SWilliam.Kucharski@Sun.COM /* Do the actual freeing of memory.  This is split out from
135*8044SWilliam.Kucharski@Sun.COM  * forget_base_memory() so that it may be called separately.  It
136*8044SWilliam.Kucharski@Sun.COM  * should be called whenever base memory is deallocated by an external
137*8044SWilliam.Kucharski@Sun.COM  * entity (if we can detect that it has done so) so that we get the
138*8044SWilliam.Kucharski@Sun.COM  * chance to free up our own blocks.
139*8044SWilliam.Kucharski@Sun.COM  */
free_unused_base_memory(void)140*8044SWilliam.Kucharski@Sun.COM void free_unused_base_memory ( void ) {
141*8044SWilliam.Kucharski@Sun.COM 	free_base_memory_block_t *free_block = NULL;
142*8044SWilliam.Kucharski@Sun.COM 
143*8044SWilliam.Kucharski@Sun.COM 	/* Try to release memory back to the BIOS.  Free all
144*8044SWilliam.Kucharski@Sun.COM 	 * consecutive blocks marked as free.
145*8044SWilliam.Kucharski@Sun.COM 	 */
146*8044SWilliam.Kucharski@Sun.COM 	while ( 1 ) {
147*8044SWilliam.Kucharski@Sun.COM 		/* Calculate address of next potential free block */
148*8044SWilliam.Kucharski@Sun.COM 		free_block = ( free_base_memory_block_t * )
149*8044SWilliam.Kucharski@Sun.COM 			phys_to_virt ( *fbms << 10 );
150*8044SWilliam.Kucharski@Sun.COM 
151*8044SWilliam.Kucharski@Sun.COM 		/* Stop processing if we're all the way up to 640K or
152*8044SWilliam.Kucharski@Sun.COM 		 * if this is not a free block
153*8044SWilliam.Kucharski@Sun.COM 		 */
154*8044SWilliam.Kucharski@Sun.COM 		if ( ( *fbms == BASE_MEMORY_MAX ) ||
155*8044SWilliam.Kucharski@Sun.COM 		     ( free_block->magic != FREE_BLOCK_MAGIC ) ) {
156*8044SWilliam.Kucharski@Sun.COM 			break;
157*8044SWilliam.Kucharski@Sun.COM 		}
158*8044SWilliam.Kucharski@Sun.COM 
159*8044SWilliam.Kucharski@Sun.COM 		/* Return memory to BIOS */
160*8044SWilliam.Kucharski@Sun.COM 		*fbms += free_block->size_kb;
161*8044SWilliam.Kucharski@Sun.COM 
162*8044SWilliam.Kucharski@Sun.COM #ifdef DEBUG_BASEMEM
163*8044SWilliam.Kucharski@Sun.COM 		printf ( "Freed %d kB base memory, %d kB now free\n",
164*8044SWilliam.Kucharski@Sun.COM 			 free_block->size_kb, *fbms );
165*8044SWilliam.Kucharski@Sun.COM 
166*8044SWilliam.Kucharski@Sun.COM 		/* Zero out freed block.  We do this in case
167*8044SWilliam.Kucharski@Sun.COM 		 * the block contained any structures that
168*8044SWilliam.Kucharski@Sun.COM 		 * might be located by scanning through
169*8044SWilliam.Kucharski@Sun.COM 		 * memory.
170*8044SWilliam.Kucharski@Sun.COM 		 */
171*8044SWilliam.Kucharski@Sun.COM 		memset ( free_block, 0, free_block->size_kb << 10 );
172*8044SWilliam.Kucharski@Sun.COM #endif
173*8044SWilliam.Kucharski@Sun.COM 	}
174*8044SWilliam.Kucharski@Sun.COM 
175*8044SWilliam.Kucharski@Sun.COM 	/* Adjust real mode stack pointer */
176*8044SWilliam.Kucharski@Sun.COM 	adjust_real_mode_stack ();
177*8044SWilliam.Kucharski@Sun.COM }
178*8044SWilliam.Kucharski@Sun.COM 
179