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