From 0b0e34e667bca9e2b1bf451e52a2b7d3ef62cb25 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 13 Jan 2007 23:57:31 +0000 Subject: [PATCH] Provide {get,set}_fbms() in basemem.h. set_fbms() will also update the E820 hidden region. --- src/arch/i386/firmware/pcbios/basemem.c | 211 ++++-------------------- src/arch/i386/firmware/pcbios/hidemem.c | 15 +- src/arch/i386/include/basemem.h | 52 +++--- 3 files changed, 68 insertions(+), 210 deletions(-) diff --git a/src/arch/i386/firmware/pcbios/basemem.c b/src/arch/i386/firmware/pcbios/basemem.c index 0c149a5e7..b126d2a70 100644 --- a/src/arch/i386/firmware/pcbios/basemem.c +++ b/src/arch/i386/firmware/pcbios/basemem.c @@ -1,185 +1,44 @@ -#include "stdint.h" -#include "stddef.h" -#include "memsizes.h" -#include "etherboot.h" -#include "basemem.h" - -/* Routines to allocate base memory in a BIOS-compatible way, by - * updating the Free Base Memory Size counter at 40:13h. - * - * Michael Brown (mcb30) - * - * We no longer have anything to do with the real-mode stack. The - * only code that can end up creating a huge bubble of wasted base - * memory is the UNDI driver, so we make it the responsibility of the - * UNDI driver to reallocate the real-mode stack if required. - */ - -/* "fbms" is an alias to the BIOS FBMS counter at 40:13, and acts just - * like any other uint16_t. We can't be used under -DKEEP_IT_REAL - * anyway, so we may as well be efficient. - */ -#define fbms ( * ( ( uint16_t * ) phys_to_virt ( 0x413 ) ) ) -#define FBMS_MAX ( 640 ) - -/* Local prototypes */ -static void free_unused_base_memory ( void ); - /* - * Return amount of free base memory in bytes + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Base memory allocation * */ -unsigned int get_free_base_memory ( void ) { - return fbms << 10; -} -/* Allocate N bytes of base memory. Amount allocated will be rounded - * up to the nearest kB, since that's the granularity of the BIOS FBMS - * counter. Returns NULL if memory cannot be allocated. +/** + * Set the BIOS free base memory counter * + * @v new_fbms New free base memory counter (in kB) */ -void * alloc_base_memory ( size_t size ) { - unsigned int size_kb = ( size + 1023 ) >> 10; - void *ptr; +void set_fbms ( unsigned int new_fbms ) { + uint16_t fbms = new_fbms; - DBG ( "Trying to allocate %d bytes of base memory from %d kB free\n", - size, fbms ); + /* Update the BIOS memory counter */ + put_real ( fbms, BDA_SEG, BDA_FBMS ); - /* Free up any unused memory before we start */ - free_unused_base_memory(); - - /* Check available base memory */ - if ( size_kb > fbms ) { - DBG ( "Could not allocate %d kB of base memory: " - "only %d kB free\n", size_kb, fbms ); - return NULL; - } - - /* Reduce available base memory */ - fbms -= size_kb; - - /* Calculate address of memory allocated */ - ptr = phys_to_virt ( fbms << 10 ); - - /* Zero out memory. We do this so that allocation of - * already-used space will show up in the form of a crash as - * soon as possible. - * - * Update: there's another reason for doing this. If we don't - * zero the contents, then they could still retain our "free - * block" markers and be liable to being freed whenever a - * base-memory allocation routine is next called. - */ - memset ( ptr, 0, size_kb << 10 ); - - DBG ( "Allocated %d kB of base memory at [%4.4lx:0000,%4.4lx:0000), " - "%d kB now free\n", size_kb, - ( virt_to_phys ( ptr ) >> 4 ), - ( ( virt_to_phys ( ptr ) + ( size_kb << 10 ) ) >> 4 ), fbms ); - - /* Update our memory map */ - get_memsizes(); - - return ptr; -} - -/* Free base memory allocated by alloc_base_memory. The BIOS provides - * nothing better than a LIFO mechanism for freeing memory (i.e. it - * just has the single "total free memory" counter), but we improve - * upon this slightly; as long as you free all the allocated blocks, it - * doesn't matter what order you free them in. (This will only work - * for blocks that are freed via free_base_memory()). - * - * Yes, it's annoying that you have to remember the size of the blocks - * you've allocated. However, since our granularity of allocation is - * 1K, the alternative is to risk wasting the occasional kB of base - * memory, which is a Bad Thing. Really, you should be using as - * little base memory as possible, so consider the awkwardness of the - * API to be a feature! :-) - * - */ -void free_base_memory ( void *ptr, size_t size ) { - unsigned int remainder = virt_to_phys ( ptr ) & 1023; - unsigned int size_kb = ( size + remainder + 1023 ) >> 10; - union free_base_memory_block *free_block = - ( ( void * ) ( ptr - remainder ) ); - - if ( ( ptr == NULL ) || ( size == 0 ) ) { - return; - } - - DBG ( "Trying to free %d bytes base memory at %4.4lx:%4.4lx " - "from %d kB free\n", size, - ( virt_to_phys ( ptr - remainder ) >> 4 ), - ( virt_to_phys ( ptr - remainder ) & 0xf ) + remainder, - fbms ); - - /* Mark every kilobyte within this block as free. This is - * overkill for normal purposes, but helps when something has - * allocated base memory with a granularity finer than the - * BIOS granularity of 1kB. PXE ROMs tend to do this when - * they allocate their own memory. This method allows us to - * free their blocks (admittedly in a rather dangerous, - * tread-on-anything-either-side sort of way, but there's no - * other way to do it). - * - * Since we're marking every kB as free, there's actually no - * need for recording the size of the blocks. However, we - * keep this in so that debug messages are friendlier. It - * probably adds around 8 bytes to the overall code size. - */ - for ( ; size_kb > 0 ; free_block++, size_kb-- ) { - /* Mark this block as unused */ - free_block->header.magic = FREE_BLOCK_MAGIC; - free_block->header.size_kb = size_kb; - } - - /* Free up unused base memory */ - free_unused_base_memory(); - - /* Update our memory map */ - get_memsizes(); -} - -/* Do the actual freeing of memory. This is split out from - * free_base_memory() so that it may be called separately. It - * should be called whenever base memory is deallocated by an external - * entity (if we can detect that it has done so) so that we get the - * chance to free up our own blocks. - */ -static void free_unused_base_memory ( void ) { - union free_base_memory_block *free_block; - - /* Try to release memory back to the BIOS. Free all - * consecutive blocks marked as free. - */ - while ( 1 ) { - /* Calculate address of next potential free block */ - free_block = phys_to_virt ( fbms << 10 ); - - /* Stop processing if we're all the way up to 640K or - * if this is not a free block - */ - if ( ( fbms == FBMS_MAX ) || - ( free_block->header.magic != FREE_BLOCK_MAGIC ) ) { - break; - } - - /* Return memory to BIOS */ - fbms += free_block->header.size_kb; - - DBG ( "Freed %ld kB of base memory at [%4.4lx:0000,%4.4lx:0000), " - "%d kB now free\n", - free_block->header.size_kb, - ( virt_to_phys ( free_block ) >> 4 ), - ( ( virt_to_phys ( free_block ) + - ( free_block->header.size_kb << 10 ) ) >> 4 ), - fbms ); - - /* Do not zero out the freed block, because it might - * be the one containing librm, in which case we're - * going to have severe problems the next time we use - * DBG() or, failing that, call get_memsizes(). - */ - } + /* Update our hidden memory region map */ + hide_basemem(); } diff --git a/src/arch/i386/firmware/pcbios/hidemem.c b/src/arch/i386/firmware/pcbios/hidemem.c index 3396e1e68..fa58135ee 100644 --- a/src/arch/i386/firmware/pcbios/hidemem.c +++ b/src/arch/i386/firmware/pcbios/hidemem.c @@ -16,8 +16,8 @@ */ #include -#include #include +#include #include /** Alignment for hidden memory regions */ @@ -71,10 +71,12 @@ void hide_region ( unsigned int region_id, physaddr_t start, physaddr_t end ) { /* Some operating systems get a nasty shock if a region of the * E820 map seems to start on a non-page boundary. Make life * safer by rounding out our edited region. - */ + */ region->start = ( start & ~( ALIGN_HIDDEN - 1 ) ); region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) ); - DBG ( "Hiding [%lx,%lx)\n", region->start, region->end ); + + DBG ( "Hiding region %d [%lx,%lx)\n", + region_id, region->start, region->end ); } /** @@ -94,15 +96,12 @@ static void hide_text ( void ) { * Hide used base memory * */ -static void hide_basemem ( void ) { - uint16_t fbms; - +void hide_basemem ( void ) { /* Hide from the top of free base memory to 640kB. Don't use * hide_region(), because we don't want this rounded to the * nearest page boundary. */ - get_real ( fbms, BDA_SEG, BDA_FBMS ); - hidden_regions[BASEMEM].start = ( fbms * 1024 ); + hidden_regions[BASEMEM].start = ( get_fbms() * 1024 ); } /** diff --git a/src/arch/i386/include/basemem.h b/src/arch/i386/include/basemem.h index 289824eb0..cd5668e01 100644 --- a/src/arch/i386/include/basemem.h +++ b/src/arch/i386/include/basemem.h @@ -1,33 +1,33 @@ -#ifndef BASEMEM_H -#define BASEMEM_H +#ifndef _BASEMEM_H +#define _BASEMEM_H -#ifdef ASSEMBLY +/** @file + * + * Base memory allocation + * + */ -/* Must match sizeof(struct free_base_memory_header) */ -#define FREE_BASEMEM_HEADER_SIZE 8 +#include +#include +#include -#else /* ASSEMBLY */ +/** + * Read the BIOS free base memory counter + * + * @ret fbms Free base memory counter (in kB) + */ +static inline unsigned int get_fbms ( void ) { + uint16_t fbms; -#include "stdint.h" + get_real ( fbms, BDA_SEG, BDA_FBMS ); + return fbms; +} -/* Structures that we use to represent a free block of base memory */ +extern void set_fbms ( unsigned int new_fbms ); -#define FREE_BLOCK_MAGIC ( ('!'<<0) + ('F'<<8) + ('R'<<16) + ('E'<<24) ) -struct free_base_memory_header { - uint32_t magic; - uint32_t size_kb; -}; +/* Actually in hidemem.c, but putting it here avoids polluting the + * architecture-independent include/hidemem.h. + */ +extern void hide_basemem ( void ); -union free_base_memory_block { - struct free_base_memory_header header; - char bytes[1024]; -}; - -/* Function prototypes */ -extern unsigned int get_free_base_memory ( void ); -extern void * alloc_base_memory ( size_t size ); -extern void free_base_memory ( void *ptr, size_t size ); - -#endif /* ASSEMBLY */ - -#endif /* BASEMEM_H */ +#endif /* _BASEMEM_H */