summaryrefslogtreecommitdiff
path: root/kernel/old/kmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/old/kmem.c')
-rw-r--r--kernel/old/kmem.c699
1 files changed, 0 insertions, 699 deletions
diff --git a/kernel/old/kmem.c b/kernel/old/kmem.c
deleted file mode 100644
index febc6b9..0000000
--- a/kernel/old/kmem.c
+++ /dev/null
@@ -1,699 +0,0 @@
-/**
-** @file kmem.c
-**
-** @author Warren R. Carithers
-** @author Kenneth Reek
-** @author 4003-506 class of 20013
-**
-** @brief Functions to perform dynamic memory allocation in the OS.
-**
-** NOTE: these should NOT be called by user processes!
-**
-** This allocator functions as a simple "slab" allocator; it allows
-** allocation of either 4096-byte ("page") or 1024-byte ("slice")
-** chunks of memory from the free pool. The free pool is initialized
-** using the memory map provided by the BIOS during the boot sequence,
-** and contains a series of blocks which are each one page of memory
-** (4KB, and aligned at 4KB boundaries); they are held in the free list
-** in LIFO order, as all pages are created equal.
-**
-** By default, the addresses used are virtual addresses rather than
-** physical addresses. Define the symbol USE_PADDRS when compiling to
-** change this.
-**
-** Each allocator ("page" and "slice") allocates the first block from
-** the appropriate free list. On deallocation, the block is added back
-** to the free list.
-**
-** The "slice" allocator operates by taking blocks from the "page"
-** allocator and splitting them into four 1K slices, which it then
-** manages. Requests are made for slices one at a time. If the free
-** list contains an available slice, it is unlinked and returned;
-** otherwise, a page is requested from the page allocator, split into
-** slices, and the slices are added to the free list, after which the
-** first one is returned. The slice free list is a simple linked list
-** of these 1K blocks; because they are all the same size, no ordering
-** is done on the free list, and no coalescing is performed.
-**
-** This could be converted into a bitmap-based allocator pretty easily.
-** A 4GB address space contains 2^20 (1,048,576) pages; at one bit per
-** page frame, that's 131,072 (2^17) bytes to cover all of the address
-** space, and that could be reduced by restricting allocatable space
-** to a subset of the 4GB space.
-**
-** Compilation options:
-**
-** ALLOC_FAIL_PANIC if an internal slice allocation fails, panic
-** USE_PADDRS build the free list using physical, not
-** virtual, addresses
-*/
-
-#define KERNEL_SRC
-
-#include <common.h>
-
-// all other framework includes are next
-#include <lib.h>
-
-#include <kmem.h>
-
-#include <list.h>
-#include <x86/arch.h>
-#include <x86/bios.h>
-#include <bootstrap.h>
-#include <cio.h>
-#include <vm.h>
-
-/*
-** PRIVATE DEFINITIONS
-*/
-
-// combination tracing tests
-#define ANY_KMEM (TRACING_KMEM|TRACING_KMEM_INIT|TRACING_KMEM_FREELIST)
-#define KMEM_OR_INIT (TRACING_KMEM|TRACING_KMEM_INIT)
-
-// parameters related to word and block sizes
-
-#define WORD_SIZE sizeof(int)
-#define LOG2_OF_WORD_SIZE 2
-
-#define LOG2_OF_PAGE_SIZE 12
-
-#define LOG2_OF_SLICE_SIZE 10
-
-// converters: pages to bytes, bytes to pages
-
-#define P2B(x) ((x) << LOG2_OF_PAGE_SIZE)
-#define B2P(x) ((x) >> LOG2_OF_PAGE_SIZE)
-
-/*
-** Name: adjacent
-**
-** Arguments: addresses of two blocks
-**
-** Description: Determines whether the second block immediately
-** follows the first one.
-*/
-#define adjacent(first,second) \
- ( (void *) (first) + P2B((first)->pages) == (void *) (second) )
-
-/*
-** PRIVATE DATA TYPES
-*/
-
-/*
-** Memory region information returned by the BIOS
-**
-** This data consists of a 32-bit integer followed
-** by an array of region descriptor structures.
-*/
-
-// a handy union for playing with 64-bit addresses
-typedef union b64_u {
- uint32_t part[2];
- uint64_t all;
-} b64_t;
-
-// the halves of a 64-bit address
-#define LOW part[0]
-#define HIGH part[1]
-
-// memory region descriptor
-typedef struct memregion_s {
- b64_t base; // base address
- b64_t length; // region length
- uint32_t type; // type of region
- uint32_t acpi; // ACPI 3.0 info
-} ATTR_PACKED region_t;
-
-/*
-** Region types
-*/
-
-#define REGION_USABLE 1
-#define REGION_RESERVED 2
-#define REGION_ACPI_RECL 3
-#define REGION_ACPI_NVS 4
-#define REGION_BAD 5
-
-/*
-** ACPI 3.0 bit fields
-*/
-
-#define REGION_IGNORE 0x01
-#define REGION_NONVOL 0x02
-
-/*
-** 32-bit and 64-bit address values as 64-bit literals
-*/
-
-#define ADDR_BIT_32 0x0000000100000000LL
-#define ADDR_LOW_HALF 0x00000000ffffffffLL
-#define ADDR_HIGH_HALR 0xffffffff00000000LL
-
-#define ADDR_32_MAX ADDR_LOW_HALF
-#define ADDR_64_FIRST ADDR_BIT_32
-
-/*
-** PRIVATE GLOBAL VARIABLES
-*/
-
-// freespace pools
-static list_t free_pages;
-static list_t free_slices;
-
-// block counts
-static uint32_t n_pages;
-static uint32_t n_slices;
-
-// initialization status
-static int km_initialized;
-
-/*
-** IMPORTED GLOBAL VARIABLES
-*/
-
-// this is no longer used; for simple situations, it can be used as
-// the KM_LOW_CUTOFF value
-//
-// extern int _end; // end of the BSS section - provided by the linker
-
-/*
-** FUNCTIONS
-*/
-
-/*
-** FREE LIST MANAGEMENT
-*/
-
-/**
-** Name: add_block
-**
-** Add a block to the free list. The block will be split into separate
-** page-sized fragments which will each be added to the free_pages
-** list; each of these will also be modified.
-**
-** @param[in] base Base physical address of the block
-** @param[in] length Block length, in bytes
-*/
-static void add_block( uint32_t base, uint32_t length ) {
-
- // don't add it if it isn't at least 4K
- if( length < SZ_PAGE ) {
- return;
- }
-
-#if ANY_KMEM
- cio_printf( " add(%08x,%08x): ", base, length );
-#endif
-
- // verify that the base address is a 4K boundary
- if( (base & MOD4K_BITS) != 0 ) {
- // nope - how many bytes will we lose from the beginning
- uint_t loss = base & MOD4K_BITS;
- // adjust the starting address: (n + 4K - 1) / 4K
- base = (base + MOD4K_BITS) & MOD4K_MASK;
- // adjust the length
- length -= loss;
- }
-
- // only want to add multiples of 4K; check the lower bits
- if( (length & MOD4K_BITS) != 0 ) {
- // round it down to 4K
- length &= MOD4K_MASK;
- }
-
- // determine the starting and ending addresses for the block
-#ifndef USE_PADDRS
- // starting and ending addresses as virtual addresses
- base = P2V(base);
-#endif
- // endpoint of the block
- uint32_t blend = base + length;
-
- // page count for this block
- int npages = 0;
-
-#if ANY_KMEM
- cio_printf( "-> base %08x len %08x: ", base, length );
-#endif
-
- // iterate through the block page by page
- while( base < blend ) {
- list_add( &free_pages, (void *) base );
- ++npages;
- base += SZ_PAGE;
- }
-
- // add the count to our running total
- n_pages += npages;
-
-#if ANY_KMEM
- cio_printf( " -> %d pages\n", npages );
-#endif
-}
-
-/**
-** Name: km_init
-**
-** Find what memory is present on the system and
-** construct the list of free memory blocks.
-**
-** Dependencies:
-** Must be called before any other init routine that uses
-** dynamic storage is called.
-*/
-void km_init( void ) {
- int32_t entries;
- region_t *region;
-
-#if TRACING_INIT
- // announce that we're starting initialization
- cio_puts( " Kmem" );
-#endif
-
- // initially, nothing in the free lists
- free_slices.next = NULL;
- free_pages.next = NULL;
- n_pages = n_slices = 0;
- km_initialized = 0;
-
- // get the list length
- entries = *((int32_t *) MMAP_ADDR);
-
-#if KMEM_OR_INIT
- cio_printf( "\nKmem: %d regions\n", entries );
-#endif
-
- // if there are no entries, we have nothing to do!
- if( entries < 1 ) { // note: entries == -1 could occur!
- return;
- }
-
- // iterate through the entries, adding things to the freelist
-
- region = ((region_t *) (MMAP_ADDR + 4));
-
- for( int i = 0; i < entries; ++i, ++region ) {
-
-#if KMEM_OR_INIT
- // report this region
- cio_printf( "%3d: ", i );
- cio_printf( " B %08x%08x",
- region->base.HIGH, region->base.LOW );
- cio_printf( " L %08x%08x",
- region->length.HIGH, region->length.LOW );
- cio_printf( " T %08x A %08x",
- region->type, region->acpi );
-#endif
-
- /*
- ** Determine whether or not we should ignore this region.
- **
- ** We ignore regions for several reasons:
- **
- ** ACPI indicates it should be ignored
- ** ACPI indicates it's non-volatile memory
- ** Region type isn't "usable"
- ** Region is above our address limit
- **
- ** Currently, only "normal" (type 1) regions are considered
- ** "usable" for our purposes. We could potentially expand
- ** this to include ACPI "reclaimable" memory.
- */
-
- // first, check the ACPI one-bit flags
-
- if( ((region->acpi) & REGION_IGNORE) == 0 ) {
-#if KMEM_OR_INIT
- cio_puts( " IGN\n" );
-#endif
- continue;
- }
-
- if( ((region->acpi) & REGION_NONVOL) != 0 ) {
-#if KMEM_OR_INIT
- cio_puts( " NVOL\n" );
-#endif
- continue; // we'll ignore this, too
- }
-
- // next, the region type
-
- if( (region->type) != REGION_USABLE ) {
-#if KMEM_OR_INIT
- cio_puts( " RCLM\n" );
-#endif
- continue; // we won't attempt to reclaim ACPI memory (yet)
- }
-
- /*
- ** We have a "normal" memory region. We need to verify
- ** that it's within our constraints.
- **
- ** We ignore anything below our KM_LOW_CUTOFF address. (In theory,
- ** we should be able to re-use much of that space; in practice,
- ** this is safer.) We won't add anything to the free list if it is:
- **
- ** * below our KM_LOW_CUTOFF value
- ** * above out KM_HIGH_CUTOFF value.
- **
- ** For blocks which straddle one of those limits, we will
- ** split it, and only use the portion that's within those
- ** bounds.
- */
-
- // grab the two 64-bit values to simplify things
- uint64_t base = region->base.all;
- uint64_t length = region->length.all;
- uint64_t endpt = base + length;
-
- // ignore it if it's above our high cutoff point
- if( base >= KM_HIGH_CUTOFF || endpt >= KM_HIGH_CUTOFF ) {
-
- // is the whole thing too high, or just part?
- if( base >= KM_HIGH_CUTOFF ) {
- // it's all too high!
-#if KMEM_OR_INIT
- cio_puts( " HIGH\n" );
-#endif
- continue;
- }
-
- // some of it is usable - fix the end point
- endpt = KM_HIGH_CUTOFF;
- }
-
- // see if it's below our low cutoff point
- if( base < KM_LOW_CUTOFF || endpt < KM_LOW_CUTOFF ) {
-
- // is the whole thing too low, or just part?
- if( endpt < KM_LOW_CUTOFF ) {
- // it's all below the cutoff!
-#if KMEM_OR_INIT
- cio_puts( " LOW\n" );
-#endif
- continue;
- }
-
- // some of it is usable - reset the base address
- base = KM_LOW_CUTOFF;
-
- // recalculate the length
- length = endpt - base;
- }
-
- // we survived the gauntlet - add the new block
- //
- // we may have changed the base or endpoint, so
- // we should recalculate the length
- length = endpt - base;
-
-#if KMEM_OR_INIT
- cio_puts( " OK\n" );
-#endif
-
- uint32_t b32 = base & ADDR_LOW_HALF;
- uint32_t l32 = length & ADDR_LOW_HALF;
-
- add_block( b32, l32 );
- }
-
- // record the initialization
- km_initialized = 1;
-
-#if KMEM_OR_INIT
- delay( DELAY_3_SEC );
-#endif
-}
-
-/**
-** Name: km_dump
-**
-** Dump information about the free lists to the console. By default,
-** prints only the list sizes; if 'addrs' is true, also dumps the list
-** of page addresses; if 'all' is also true, dumps page addresses and
-** slice addresses.
-**
-** @param addrs Also dump page addresses
-** @param both Also dump slice addresses
-*/
-void km_dump( bool_t addrs, bool_t both ) {
-
- // report the sizes
- cio_printf( "&free_pages %08x, &free_slices %08x, %u pages, %u slices\n",
- (uint32_t) &free_pages, (uint32_t) &free_slices,
- n_pages, n_slices );
-
- // was that all?
- if( !addrs ) {
- return;
- }
-
- // dump the addresses of the pages in the free list
- uint32_t n = 0;
- list_t *block = free_pages.next;
- while( block != NULL ) {
- if( n && !(n & MOD4_BITS) ) {
- // four per line
- cio_putchar( '\n' );
- }
- cio_printf( " page @ 0x%08x", (uint32_t) block );
- block = block->next;
- ++n;
- }
-
- // sanity check - verify that the counts match
- if( n != n_pages ) {
- sprint( b256, "km_dump: n_pages %u, counted %u!!!\n",
- n_pages, n );
- WARNING( b256);
- }
-
- if( !both ) {
- return;
- }
-
- // but wait - there's more!
-
- // also dump the addresses of slices in the slice free list
- n = 0;
- block = free_slices.next;
- while( block != NULL ) {
- if( n && !(n & MOD4_BITS) ) {
- // four per line
- cio_putchar( '\n' );
- }
- cio_printf( " slc @ 0x%08x", (uint32_t) block );
- block = block->next;
- ++n;
- }
-
- // sanity check - verify that the counts match
- if( n != n_slices ) {
- sprint( b256, "km_dump: n_slices %u, counted %u!!!\n",
- n_slices, n );
- WARNING( b256);
- }
-}
-
-/*
-** PAGE MANAGEMENT
-*/
-
-/**
-** Name: km_page_alloc
-**
-** Allocate a page of memory from the free list.
-**
-** @return a pointer to the beginning of the allocated page,
-** or NULL if no memory is available
-*/
-void *km_page_alloc( void ) {
-
- // if km_init() wasn't called first, stop us in our tracks
- assert( km_initialized );
-
-#if TRACING_KMEM_FREELIST
- cio_puts( "KM: pg_alloc()" );
-#endif
-
- // pointer to the first block
- void *page = list_remove( &free_pages );
-
- // was a page available?
- if( page == NULL ){
- // nope!
-#if TRACING_KMEM_FREELIST
- cio_puts( " FAIL\n" );
-#endif
-#if ALLOC_FAIL_PANIC
- PANIC( 0, "page alloc failed" );
-#else
- return NULL;
-#endif
- }
-
- // fix the count of available pages
- --n_pages;
-
-#if TRACING_KMEM_FREELIST
- cio_printf( " -> %08x\n", (uint32_t) page );
-#endif
-
- return( page );
-}
-
-/**
-** Name: km_page_free
-**
-** Returns a page to the list of available pages.
-**
-** @param[in] page Pointer to the page to be returned to the free list
-*/
-void km_page_free( void *page ){
-
- // verify that km_init() was called first
- assert( km_initialized );
-
-#if TRACING_KMEM_FREELIST
- cio_printf( "KM: pg_free(%08x)\n", (uint32_t) page );
-#endif
-
- /*
- ** Don't do anything if the address is NULL.
- */
- if( page == NULL ){
- return;
- }
-
-
- /*
- ** CRITICAL ASSUMPTION
- **
- ** We assume that the block pointer given to us points to a single
- ** page-sized block of memory. We make this assumption because we
- ** don't track allocation sizes. We can't use the simple "allocate
- ** four extra bytes before the returned pointer" scheme to do this
- ** because we're managing pages, and the pointers we return must point
- ** to page boundaries, so we would wind up allocating an extra page
- ** for each allocation.
- **
- ** Alternatively, we could keep an array of addresses and block
- ** sizes ourselves, but that feels clunky, and would risk running out
- ** of table entries if there are lots of allocations (assuming we use
- ** a 4KB page to hold the table, at eight bytes per entry we would have
- ** 512 entries per page).
- **
- ** IF THIS ASSUMPTION CHANGES, THIS CODE MUST BE FIXED!!!
- */
-
- // link this into the free list
- list_add( &free_pages, page );
-
- // one more in the pool
- ++n_pages;
-}
-
-/*
-** SLICE MANAGEMENT
-*/
-
-/*
-** Slices are 1024-byte fragments from pages. We maintain a free list of
-** slices for those parts of the OS which don't need full 4096-byte chunks
-** of space.
-*/
-
-/**
-** Name: carve_slices
-**
-** Split an allocated page into four slices and add
-** them to the "free slices" list.
-**
-** @param page Pointer to the page to be carved up
-*/
-static void carve_slices( void *page ) {
-
- // sanity check
- assert1( page != NULL );
-
-#if TRACING_KMEM_FREELIST
- cio_printf( "KM: carve_slices(%08x)\n", (uint32_t) page );
-#endif
-
- // create the four slices from it
- uint8_t *ptr = (uint8_t *) page;
- for( int i = 0; i < 4; ++i ) {
- km_slice_free( (void *) ptr );
- ptr += SZ_SLICE;
- ++n_slices;
- }
-}
-
-/**
-** Name: km_slice_alloc
-**
-** Dynamically allocates a slice (1/4 of a page). If no
-** memory is available, we return NULL (unless ALLOC_FAIL_PANIC
-** was defined, in which case we panic).
-**
-** @return a pointer to the allocated slice
-*/
-void *km_slice_alloc( void ) {
-
- // verify that km_init() was called first
- assert( km_initialized );
-
-#if TRACING_KMEM_FREELIST
- cio_printf( "KM: sl_alloc()\n" );
-#endif
-
- // if we are out of slices, create a few more
- if( free_slices.next == NULL ) {
- void *new = km_page_alloc();
- if( new == NULL ) {
- // can't get any more space
-#if ALLOC_FAIL_PANIC
- PANIC( 0, "slice new alloc failed" );
-#else
- return NULL;
-#endif
- }
- carve_slices( new );
- }
-
- // take the first one from the free list
- void *slice = list_remove( &free_slices );
- assert( slice != NULL );
- --n_slices;
-
- // make it nice and shiny for the caller
- memclr( (void *) slice, SZ_SLICE );
-
- return( slice );
-}
-
-/**
-** Name: km_slice_free
-**
-** Returns a slice to the list of available slices.
-**
-** We make no attempt to merge slices, as we treat them as
-** independent blocks of memory (like pages).
-**
-** @param[in] block Pointer to the slice (1/4 page) to be freed
-*/
-void km_slice_free( void *block ) {
-
- // verify that km_init() was called first
- assert( km_initialized );
-
-#if TRACING_KMEM_FREELIST
- cio_printf( "KM: sl_free(%08x)\n", (uint32_t) block );
-#endif
-
- // just add it to the front of the free list
- list_add( &free_slices, block );
- --n_slices;
-}