diff options
Diffstat (limited to 'c/src/lib/libbsp/powerpc/motorola_powerpc/bootloader/mm.c')
-rw-r--r-- | c/src/lib/libbsp/powerpc/motorola_powerpc/bootloader/mm.c | 982 |
1 files changed, 0 insertions, 982 deletions
diff --git a/c/src/lib/libbsp/powerpc/motorola_powerpc/bootloader/mm.c b/c/src/lib/libbsp/powerpc/motorola_powerpc/bootloader/mm.c deleted file mode 100644 index 3807c75d85..0000000000 --- a/c/src/lib/libbsp/powerpc/motorola_powerpc/bootloader/mm.c +++ /dev/null @@ -1,982 +0,0 @@ -/* - * mm.c -- Crude memory management for early boot. - * - * Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es - * - * Modified to compile in RTEMS development environment - * by Eric Valette - * - * Copyright (C) 1999 Eric Valette. valette@crf.canon.fr - * - * The license and distribution terms for this file may be - * found in found in the file LICENSE in this distribution or at - * http://www.OARcorp.com/rtems/license.html. - * - * $Id$ - */ - -/* This code is a crude memory manager for early boot for LinuxPPC. - * As such, it does not try to perform many optimiztions depending - * on the processor, it only uses features which are common to - * all processors (no BATs...). - * - * On PreP platorms (the only ones on which it works for now), - * it maps 1:1 all RAM/ROM and I/O space as claimed by the - * residual data. The holes between these areas can be virtually - * remapped to any of these, since for some functions it is very handy - * to have virtually contiguous but physically discontiguous memory. - * - * Physical memory allocation is also very crude, since it's only - * designed to manage a small number of large chunks. For valloc/vfree - * and palloc/pfree, the unit of allocation is the 4kB page. - * - * The salloc/sfree has been added after tracing gunzip and seeing - * how it performed a very large number of small allocations. - * For these the unit of allocation is 8 bytes (the s stands for - * small or subpage). This memory is cleared when allocated. - * - */ - -#include <sys/types.h> -#include <libcpu/spr.h> -#include "bootldr.h" -#include <libcpu/mmu.h> -#include <libcpu/page.h> -#include <limits.h> - -/* We use our own kind of simple memory areas for the loader, but - * we want to avoid potential clashes with kernel includes. - * Here a map maps contiguous areas from base to end, - * the firstpte entry corresponds to physical address and has the low - * order bits set for caching and permission. - */ - -typedef struct _map { - struct _map *next; - u_long base; - u_long end; - u_long firstpte; -} map; - -/* The LSB of the firstpte entries on map lists other than mappings - * are constants which can be checked for debugging. All these constants - * have bit of weight 4 set, this bit is zero in the mappings list entries. - * Actually firstpte&7 value is: - * - 0 or 1 should not happen - * - 2 for RW actual virtual->physical mappings - * - 3 for RO actual virtual->physical mappings - * - 6 for free areas to be suballocated by salloc - * - 7 for salloc'ated areas - * - 4 or 5 for all others, in this case firtpte & 63 is - * - 4 for unused maps (on the free list) - * - 12 for free physical memory - * - 13 for physical memory in use - * - 20 for free virtual address space - * - 21 for allocated virtual address space - * - 28 for physical memory space suballocated by salloc - * - 29 for physical memory that can't be freed - */ - -#define MAP_FREE_SUBS 6 -#define MAP_USED_SUBS 7 - -#define MAP_FREE 4 -#define MAP_FREE_PHYS 12 -#define MAP_USED_PHYS 13 -#define MAP_FREE_VIRT 20 -#define MAP_USED_VIRT 21 -#define MAP_SUBS_PHYS 28 -#define MAP_PERM_PHYS 29 - -SPR_RW(SDR1); -SPR_RO(DSISR); -SPR_RO(DAR); - -/* We need a few statically allocated free maps to bootstrap the - * memory managment */ -static map free_maps[4] = {{free_maps+1, 0, 0, MAP_FREE}, - {free_maps+2, 0, 0, MAP_FREE}, - {free_maps+3, 0, 0, MAP_FREE}, - {NULL, 0, 0, MAP_FREE}}; -struct _mm_private { - void *sdr1; - u_long hashmask; - map *freemaps; /* Pool of unused map structs */ - map *mappings; /* Sorted list of virtual->physical mappings */ - map *physavail; /* Unallocated physical address space */ - map *physused; /* Allocated physical address space */ - map *physperm; /* Permanently allocated physical space */ - map *virtavail; /* Unallocated virtual address space */ - map *virtused; /* Allocated virtual address space */ - map *sallocfree; /* Free maps for salloc */ - map *sallocused; /* Used maps for salloc */ - map *sallocphys; /* Physical areas used by salloc */ - u_int hashcnt; /* Used to cycle in PTEG when they overflow */ -} mm_private = {hashmask: 0xffc0, - freemaps: free_maps+0}; - -/* A simplified hash table entry declaration */ -typedef struct _hash_entry { - int key; - u_long rpn; -} hash_entry; - -void print_maps(map *, const char *); - -/* The handler used for all exceptions although for now it is only - * designed to properly handle MMU interrupts to fill the hash table. - */ - - -void _handler(int vec, ctxt *p) { - map *area; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - u_long vaddr, cause; - if (vec==4 || vec==7) { /* ISI exceptions are different */ - vaddr = p->nip; - cause = p->msr; - } else { /* Valid for DSI and alignment exceptions */ - vaddr = _read_DAR(); - cause = _read_DSISR(); - } - - if (vec==3 || vec==4) { - /* Panic if the fault is not PTE not found. */ - if (!(cause & 0x40000000)) { - MMUon(); - printk("\nPanic: vector=%x, cause=%lx\n", vec, cause); - hang("Memory protection violation at ", vaddr, p); - } - - for(area=mm->mappings; area; area=area->next) { - if(area->base<=vaddr && vaddr<=area->end) break; - } - - if (area) { - u_long hash, vsid, rpn; - hash_entry volatile *hte, *_hte1; - u_int i, alt=0, flushva; - - vsid = _read_SR((void *)vaddr); - rpn = (vaddr&PAGE_MASK)-area->base+area->firstpte; - hash = vsid<<6; - hash ^= (vaddr>>(PAGE_SHIFT-6))&0x3fffc0; - hash &= mm->hashmask; - /* Find an empty entry in the PTEG, else - * replace a random one. - */ - hte = (hash_entry *) ((u_long)(mm->sdr1)+hash); - for (i=0; i<8; i++) { - if (hte[i].key>=0) goto found; - } - hash ^= mm->hashmask; - alt = 0x40; _hte1 = hte; - hte = (hash_entry *) ((u_long)(mm->sdr1)+hash); - - for (i=0; i<8; i++) { - if (hte[i].key>=0) goto found; - } - alt = 0; - hte = _hte1; - /* Chose a victim entry and replace it. There might be - * better policies to choose the victim, but in a boot - * loader we want simplicity as long as it works. - * - * We would not need to invalidate the TLB entry since - * the mapping is still valid. But this would be a mess - * when unmapping so we make sure that the TLB is a - * subset of the hash table under all circumstances. - */ - i = mm->hashcnt; - mm->hashcnt = (mm->hashcnt+1)%8; - /* Note that the hash is already complemented here ! */ - flushva = (~(hash<<9)^((hte[i].key)<<5)) &0x3ff000; - if (hte[i].key&0x40) flushva^=0x3ff000; - flushva |= ((hte[i].key<<21)&0xf0000000) - | ((hte[i].key<<22)&0x0fc00000); - hte[i].key=0; - asm volatile("sync; tlbie %0; sync" : : "r" (flushva)); - found: - hte[i].rpn = rpn; - asm volatile("eieio": : ); - hte[i].key = 0x80000000|(vsid<<7)|alt| - ((vaddr>>22)&0x3f); - return; - } else { - MMUon(); - printk("\nPanic: vector=%x, cause=%lx\n", vec, cause); - hang("\nInvalid memory access attempt at ", vaddr, p); - } - } else { - MMUon(); - printk("\nPanic: vector=%x, dsisr=%lx, faultaddr =%lx, msr=%lx opcode=%lx\n", vec, - cause, p->nip, p->msr, * ((unsigned int*) p->nip) ); - if (vec == 7) { - unsigned int* ptr = ((unsigned int*) p->nip) - 4 * 10; - for (; ptr <= (((unsigned int*) p->nip) + 4 * 10); ptr ++) - printk("Hexdecimal code at address %x = %x\n", ptr, *ptr); - } - hang("Program or alignment exception at ", vaddr, p); - } -} - -/* Generic routines for map handling. - */ - -static inline -void free_map(map *p) { - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - if (!p) return; - p->next=mm->freemaps; - mm->freemaps=p; - p->firstpte=MAP_FREE; -} - -/* Sorted insertion in linked list */ -static -int insert_map(map **head, map *p) { - map *q = *head; - if (!p) return 0; - if (q && (q->base < p->base)) { - for(;q->next && q->next->base<p->base; q = q->next); - if ((q->end >= p->base) || - (q->next && p->end>=q->next->base)) { - free_map(p); - printk("Overlapping areas!\n"); - return 1; - } - p->next = q->next; - q->next = p; - } else { /* Insert at head */ - if (q && (p->end >= q->base)) { - free_map(p); - printk("Overlapping areas!\n"); - return 1; - } - p->next = q; - *head = p; - } - return 0; -} - - -/* Removal from linked list */ - -static -map *remove_map(map **head, map *p) { - map *q = *head; - - if (!p || !q) return NULL; - if (q==p) { - *head = q->next; - return p; - } - for(;q && q->next!=p; q=q->next); - if (q) { - q->next=p->next; - return p; - } else { - return NULL; - } -} - -static -map *remove_map_at(map **head, void * vaddr) { - map *p, *q = *head; - - if (!vaddr || !q) return NULL; - if (q->base==(u_long)vaddr) { - *head = q->next; - return q; - } - while (q->next && q->next->base != (u_long)vaddr) q=q->next; - p=q->next; - if (p) q->next=p->next; - return p; -} - -static inline -map * alloc_map_page(void) { - map *from, *p; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - /* printk("Allocating new map page !"); */ - /* Get the highest page */ - for (from=mm->physavail; from && from->next; from=from->next); - if (!from) return NULL; - - from->end -= PAGE_SIZE; - - mm->freemaps = (map *) (from->end+1); - - for(p=mm->freemaps; p<mm->freemaps+PAGE_SIZE/sizeof(map)-1; p++) { - p->next = p+1; - p->firstpte = MAP_FREE; - } - (p-1)->next=0; - - /* Take the last one as pointer to self and insert - * the map into the permanent map list. - */ - - p->firstpte = MAP_PERM_PHYS; - p->base=(u_long) mm->freemaps; - p->end = p->base+PAGE_SIZE-1; - - insert_map(&mm->physperm, p); - - if (from->end+1 == from->base) - free_map(remove_map(&mm->physavail, from)); - - return mm->freemaps; -} - -static -map * alloc_map(void) { - map *p; - struct _mm_private * mm = (struct _mm_private *) bd->mm_private; - - p = mm->freemaps; - if (!p) { - p=alloc_map_page(); - } - - if(p) mm->freemaps=p->next; - - return p; -} - -static -void coalesce_maps(map *p) { - while(p) { - if (p->next && (p->end+1 == p->next->base)) { - map *q=p->next; - p->end=q->end; - p->next=q->next; - free_map(q); - } else { - p = p->next; - } - } -} - -/* These routines are used to find the free memory zones to avoid - * overlapping destructive copies when initializing. - * They work from the top because of the way we want to boot. - * In the following the term zone refers to the memory described - * by one or several contiguous so called segments in the - * residual data. - */ -#define STACK_PAGES 2 -static inline u_long -find_next_zone(RESIDUAL *res, u_long lowpage, u_long flags) { - u_long i, newmin=0, size=0; - for(i=0; i<res->ActualNumMemSegs; i++) { - if (res->Segs[i].Usage & flags - && res->Segs[i].BasePage<lowpage - && res->Segs[i].BasePage>newmin) { - newmin=res->Segs[i].BasePage; - size=res->Segs[i].PageCount; - } - } - return newmin+size; -} - -static inline u_long -find_zone_start(RESIDUAL *res, u_long highpage, u_long flags) { - u_long i; - int progress; - do { - progress=0; - for (i=0; i<res->ActualNumMemSegs; i++) { - if ( (res->Segs[i].BasePage+res->Segs[i].PageCount - == highpage) - && res->Segs[i].Usage & flags) { - highpage=res->Segs[i].BasePage; - progress=1; - } - } - } while(progress); - return highpage; -} - -/* The Motorola NT firmware does not provide any setting in the residual - * data about memory segment usage. The following table provides enough - * info so that this bootloader can work. - */ -MEM_MAP seg_fix[] = { - { 0x2000, 0xFFF00, 0x00100 }, - { 0x0020, 0x02000, 0x7E000 }, - { 0x0008, 0x00800, 0x00168 }, - { 0x0004, 0x00000, 0x00005 }, - { 0x0001, 0x006F1, 0x0010F }, - { 0x0002, 0x006AD, 0x00044 }, - { 0x0010, 0x00005, 0x006A8 }, - { 0x0010, 0x00968, 0x00698 }, - { 0x0800, 0xC0000, 0x3F000 }, - { 0x0600, 0xBF800, 0x00800 }, - { 0x0500, 0x81000, 0x3E800 }, - { 0x0480, 0x80800, 0x00800 }, - { 0x0440, 0x80000, 0x00800 } }; - - -/* The Motorola NT firmware does not set up all required info in the residual - * data. This routine changes some things in a way that the bootloader and - * linux are happy. - */ -void -fix_residual( RESIDUAL *res ) -{ -#if 0 - PPC_DEVICE *hostbridge; -#endif - int i; - - /* Missing memory segment information */ - res->ActualNumMemSegs = sizeof(seg_fix)/sizeof(MEM_MAP); - for (i=0; i<res->ActualNumMemSegs; i++) { - res->Segs[i].Usage = seg_fix[i].Usage; - res->Segs[i].BasePage = seg_fix[i].BasePage; - res->Segs[i].PageCount = seg_fix[i].PageCount; - } - /* The following should be fixed in the current version of the - * kernel and of the bootloader. - */ -#if 0 - /* PPCBug has this zero */ - res->VitalProductData.CacheLineSize = 0; - /* Motorola NT firmware sets TimeBaseDivisor to 0 */ - if ( res->VitalProductData.TimeBaseDivisor == 0 ) { - res->VitalProductData.TimeBaseDivisor = 4000; - } - - /* Motorola NT firmware records the PCIBridge as a "PCIDEVICE" and - * sets "PCIBridgeDirect". This bootloader and linux works better if - * BusId = "PROCESSORDEVICE" and Interface = "PCIBridgeIndirect". - */ - hostbridge=residual_find_device(PCIDEVICE, NULL, - BridgeController, - PCIBridge, -1, 0); - if (hostbridge) { - hostbridge->DeviceId.BusId = PROCESSORDEVICE; - hostbridge->DeviceId.Interface = PCIBridgeIndirect; - } -#endif -} - -/* This routine is the first C code called with very little stack space! - * Its goal is to find where the boot image can be moved. This will - * be the highest address with enough room. - */ -int early_setup(u_long image_size) { - register RESIDUAL *res = bd->residual; - u_long minpages = PAGE_ALIGN(image_size)>>PAGE_SHIFT; - - /* Fix residual if we are loaded by Motorola NT firmware */ - if ( res && res->VitalProductData.FirmwareSupplier == 0x10000 ) - fix_residual( res ); - - /* FIXME: if OF we should do something different */ - if( !bd->of_entry && res && - res->ResidualLength <= sizeof(RESIDUAL) && res->Version == 0 ) { - u_long lowpage=ULONG_MAX, highpage; - u_long imghigh=0, stkhigh=0; - /* Find the highest and large enough contiguous zone - consisting of free and BootImage sections. */ - /* Find 3 free areas of memory, one for the main image, one - * for the stack (STACK_PAGES), and page one to put the map - * structures. They are allocated from the top of memory. - * In most cases the stack will be put just below the image. - */ - while((highpage = - find_next_zone(res, lowpage, BootImage|Free))) { - lowpage=find_zone_start(res, highpage, BootImage|Free); - if ((highpage-lowpage)>minpages && - highpage>imghigh) { - imghigh=highpage; - highpage -=minpages; - } - if ((highpage-lowpage)>STACK_PAGES && - highpage>stkhigh) { - stkhigh=highpage; - highpage-=STACK_PAGES; - } - } - - bd->image = (void *)((imghigh-minpages)<<PAGE_SHIFT); - bd->stack=(void *) (stkhigh<<PAGE_SHIFT); - - /* The code mover is put at the lowest possible place - * of free memory. If this corresponds to the loaded boot - * partition image it does not matter because it overrides - * the unused part of it (x86 code). - */ - bd->mover=(void *) (lowpage<<PAGE_SHIFT); - - /* Let us flush the caches in all cases. After all it should - * not harm even on 601 and we don't care about performance. - * Right now it's easy since all processors have a line size - * of 32 bytes. Once again residual data has proved unreliable. - */ - bd->cache_lsize = 32; - } - /* For now we always assume that it's succesful, we should - * handle better the case of insufficient memory. - */ - return 0; -} - -void * valloc(u_long size) { - map *p, *q; - struct _mm_private * mm = (struct _mm_private *) bd->mm_private; - - if (size==0) return NULL; - size=PAGE_ALIGN(size)-1; - for (p=mm->virtavail; p; p=p->next) { - if (p->base+size <= p->end) break; - } - if(!p) return NULL; - q=alloc_map(); - q->base=p->base; - q->end=q->base+size; - q->firstpte=MAP_USED_VIRT; - insert_map(&mm->virtused, q); - if (q->end==p->end) free_map(remove_map(&mm->virtavail, p)); - else p->base += size+1; - return (void *)q->base; -} - -static -void vflush(map *virtmap) { - struct _mm_private * mm = (struct _mm_private *) bd->mm_private; - u_long i, limit=(mm->hashmask>>3)+8; - hash_entry volatile *p=(hash_entry *) mm->sdr1; - - /* PTE handling is simple since the processor never update - * the entries. Writable pages always have the C bit set and - * all valid entries have the R bit set. From the processor - * point of view the hash table is read only. - */ - for (i=0; i<limit; i++) { - if (p[i].key<0) { - u_long va; - va = ((i<<9)^((p[i].key)<<5)) &0x3ff000; - if (p[i].key&0x40) va^=0x3ff000; - va |= ((p[i].key<<21)&0xf0000000) - | ((p[i].key<<22)&0x0fc00000); - if (va>=virtmap->base && va<=virtmap->end) { - p[i].key=0; - asm volatile("sync; tlbie %0; sync" : : - "r" (va)); - } - } - } -} - -void vfree(void *vaddr) { - map *physmap, *virtmap; /* Actual mappings pertaining to this vm */ - struct _mm_private * mm = (struct _mm_private *) bd->mm_private; - - /* Flush memory queues */ - asm volatile("sync": : : "memory"); - - virtmap = remove_map_at(&mm->virtused, vaddr); - if (!virtmap) return; - - /* Remove mappings corresponding to virtmap */ - for (physmap=mm->mappings; physmap; ) { - map *nextmap=physmap->next; - if (physmap->base>=virtmap->base - && physmap->base<virtmap->end) { - free_map(remove_map(&mm->mappings, physmap)); - } - physmap=nextmap; - } - - vflush(virtmap); - - virtmap->firstpte= MAP_FREE_VIRT; - insert_map(&mm->virtavail, virtmap); - coalesce_maps(mm->virtavail); -} - -void vunmap(void *vaddr) { - map *physmap, *virtmap; /* Actual mappings pertaining to this vm */ - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - /* Flush memory queues */ - asm volatile("sync": : : "memory"); - - /* vaddr must be within one of the vm areas in use and - * then must correspond to one of the physical areas - */ - for (virtmap=mm->virtused; virtmap; virtmap=virtmap->next) { - if (virtmap->base<=(u_long)vaddr && - virtmap->end>=(u_long)vaddr) break; - } - if (!virtmap) return; - - physmap = remove_map_at(&mm->mappings, vaddr); - if(!physmap) return; - vflush(physmap); - free_map(physmap); -} - -int vmap(void *vaddr, u_long p, u_long size) { - map *q; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - size=PAGE_ALIGN(size); - if(!size) return 1; - /* Check that the requested area fits in one vm image */ - for (q=mm->virtused; q; q=q->next) { - if ((q->base <= (u_long)vaddr) && - (q->end>=(u_long)vaddr+size -1)) break; - } - if (!q) return 1; - q= alloc_map(); - if (!q) return 1; - q->base = (u_long)vaddr; - q->end = (u_long)vaddr+size-1; - q->firstpte = p; - return insert_map(&mm->mappings, q); -} - -static -void create_identity_mappings(int type, int attr) { - u_long lowpage=ULONG_MAX, highpage; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - RESIDUAL * res=bd->residual; - - while((highpage = find_next_zone(res, lowpage, type))) { - map *p; - lowpage=find_zone_start(res, highpage, type); - p=alloc_map(); - /* Do not map page 0 to catch null pointers */ - lowpage = lowpage ? lowpage : 1; - p->base=lowpage<<PAGE_SHIFT; - p->end=(highpage<<PAGE_SHIFT)-1; - p->firstpte = (lowpage<<PAGE_SHIFT)|attr; - insert_map(&mm->mappings, p); - } -} - -static inline -void add_free_map(u_long base, u_long end) { - map *q=NULL; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - if (base<end) q=alloc_map(); - if (!q) return; - q->base=base; - q->end=end-1; - q->firstpte=MAP_FREE_VIRT; - insert_map(&mm->virtavail, q); -} - -static inline -void create_free_vm(void) { - map *p; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - u_long vaddr=PAGE_SIZE; /* Never map vaddr 0 */ - for(p=mm->mappings; p; p=p->next) { - add_free_map(vaddr, p->base); - vaddr=p->end+1; - } - /* Special end of memory case */ - if (vaddr) add_free_map(vaddr,0); -} - -/* Memory management initialization. - * Set up the mapping lists. - */ - -static inline -void add_perm_map(u_long start, u_long size) { - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - map *p=alloc_map(); - p->base = start; - p->end = start + size - 1; - p->firstpte = MAP_PERM_PHYS; - insert_map(& mm->physperm , p); -} - -void mm_init(u_long image_size) -{ - u_long lowpage=ULONG_MAX, highpage; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - RESIDUAL * res=bd->residual; - extern void (tlb_handlers)(void); - extern void (_handler_glue)(void); - int i; - map *p; - - /* The checks are simplified by the fact that the image - * and stack area are always allocated at the upper end - * of a free block. - */ - while((highpage = find_next_zone(res, lowpage, BootImage|Free))) { - lowpage=find_zone_start(res, highpage, BootImage|Free); - if ( ( ((u_long)bd->image+PAGE_ALIGN(image_size))>>PAGE_SHIFT) - == highpage) { - highpage=(u_long)(bd->image)>>PAGE_SHIFT; - add_perm_map((u_long)bd->image, image_size); - } - if ( (( u_long)bd->stack>>PAGE_SHIFT) == highpage) { - highpage -= STACK_PAGES; - add_perm_map(highpage<<PAGE_SHIFT, - STACK_PAGES*PAGE_SIZE); - } - /* Protect the interrupt handlers that we need ! */ - if (lowpage<2) lowpage=2; - /* Check for the special case of full area! */ - if (highpage>lowpage) { - p = alloc_map(); - p->base = lowpage<<PAGE_SHIFT; - p->end = (highpage<<PAGE_SHIFT)-1; - p->firstpte=MAP_FREE_PHYS; - insert_map(&mm->physavail, p); - } - } - - /* Allocate the hash table */ - mm->sdr1=__palloc(0x10000, PA_PERM|16); - _write_SDR1((u_long)mm->sdr1); - memset(mm->sdr1, 0, 0x10000); - mm->hashmask = 0xffc0; - - /* Setup the segment registers as we want them */ - for (i=0; i<16; i++) _write_SR(i, (void *)(i<<28)); - /* Create the maps for the physical memory, firwmarecode does not - * seem to be necessary. ROM is mapped read-only to reduce the risk - * of reprogramming it because it's often Flash and some are - * amazingly easy to overwrite. - */ - create_identity_mappings(BootImage|Free|FirmwareCode|FirmwareHeap| - FirmwareStack, PTE_RAM); - create_identity_mappings(SystemROM, PTE_ROM); - create_identity_mappings(IOMemory|SystemIO|SystemRegs| - PCIAddr|PCIConfig|ISAAddr, PTE_IO); - - create_free_vm(); - - /* Install our own MMU and trap handlers. */ - codemove((void *) 0x300, _handler_glue, 0x100, bd->cache_lsize); - codemove((void *) 0x400, _handler_glue, 0x100, bd->cache_lsize); - codemove((void *) 0x600, _handler_glue, 0x100, bd->cache_lsize); - codemove((void *) 0x700, _handler_glue, 0x100, bd->cache_lsize); -} - -void * salloc(u_long size) { - map *p, *q; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - if (size==0) return NULL; - - size = (size+7)&~7; - - for (p=mm->sallocfree; p; p=p->next) { - if (p->base+size <= p->end) break; - } - if(!p) { - void *m; - m = __palloc(size, PA_SUBALLOC); - p = alloc_map(); - if (!m && !p) return NULL; - p->base = (u_long) m; - p->firstpte = MAP_FREE_SUBS; - p->end = (u_long)m+PAGE_ALIGN(size)-1; - insert_map(&mm->sallocfree, p); - coalesce_maps(mm->sallocfree); - coalesce_maps(mm->sallocphys); - }; - q=alloc_map(); - q->base=p->base; - q->end=q->base+size-1; - q->firstpte=MAP_USED_SUBS; - insert_map(&mm->sallocused, q); - if (q->end==p->end) free_map(remove_map(&mm->sallocfree, p)); - else p->base += size; - memset((void *)q->base, 0, size); - return (void *)q->base; -} - -void sfree(void *p) { - map *q; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - q=remove_map_at(&mm->sallocused, p); - if (!q) return; - q->firstpte=MAP_FREE_SUBS; - insert_map(&mm->sallocfree, q); - coalesce_maps(mm->sallocfree); -} - -/* first/last area fit, flags is a power of 2 indicating the required - * alignment. The algorithms are stupid because we expect very little - * fragmentation of the areas, if any. The unit of allocation is the page. - * The allocation is by default performed from higher addresses down, - * unless flags&PA_LOW is true. - */ - -void * __palloc(u_long size, int flags) -{ - u_long mask = ((1<<(flags&PA_ALIGN_MASK))-1); - map *newmap, *frommap, *p, *splitmap=0; - map **queue; - u_long qflags; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - - /* Asking for a size which is not a multiple of the alignment - is likely to be an error. */ - - if (size & mask) return NULL; - size = PAGE_ALIGN(size); - if(!size) return NULL; - - if (flags&PA_SUBALLOC) { - queue = &mm->sallocphys; - qflags = MAP_SUBS_PHYS; - } else if (flags&PA_PERM) { - queue = &mm->physperm; - qflags = MAP_PERM_PHYS; - } else { - queue = &mm->physused; - qflags = MAP_USED_PHYS; - } - /* We need to allocate that one now so no two allocations may attempt - * to take the same memory simultaneously. Alloc_map_page does - * not call back here to avoid infinite recursion in alloc_map. - */ - - if (mask&PAGE_MASK) { - splitmap=alloc_map(); - if (!splitmap) return NULL; - } - - for (p=mm->physavail, frommap=NULL; p; p=p->next) { - u_long high = p->end; - u_long limit = ((p->base+mask)&~mask) + size-1; - if (high>=limit && ((p->base+mask)&~mask)+size>p->base) { - frommap = p; - if (flags&PA_LOW) break; - } - } - - if (!frommap) { - if (splitmap) free_map(splitmap); - return NULL; - } - - newmap=alloc_map(); - - if (flags&PA_LOW) { - newmap->base = (frommap->base+mask)&~mask; - } else { - newmap->base = (frommap->end +1 - size) & ~mask; - } - - newmap->end = newmap->base+size-1; - newmap->firstpte = qflags; - - /* Add a fragment if we don't allocate until the end. */ - - if (splitmap) { - splitmap->base=newmap->base+size; - splitmap->end=frommap->end; - splitmap->firstpte= MAP_FREE_PHYS; - frommap->end=newmap->base-1; - } else if (flags & PA_LOW) { - frommap->base=newmap->base+size; - } else { - frommap->end=newmap->base-1; - } - - /* Remove a fragment if it becomes empty. */ - if (frommap->base == frommap->end+1) { - free_map(remove_map(&mm->physavail, frommap)); - } - - if (splitmap) { - if (splitmap->base == splitmap->end+1) { - free_map(remove_map(&mm->physavail, splitmap)); - } else { - insert_map(&mm->physavail, splitmap); - } - } - - insert_map(queue, newmap); - return (void *) newmap->base; - -} - -void pfree(void * p) { - map *q; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - q=remove_map_at(&mm->physused, p); - if (!q) return; - q->firstpte=MAP_FREE_PHYS; - insert_map(&mm->physavail, q); - coalesce_maps(mm->physavail); -} - -#ifdef DEBUG -/* Debugging functions */ -void print_maps(map *chain, const char *s) { - map *p; - printk("%s",s); - for(p=chain; p; p=p->next) { - printk(" %08lx-%08lx: %08lx\n", - p->base, p->end, p->firstpte); - } -} - -void print_all_maps(const char * s) { - u_long freemaps; - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - map *free; - printk("%s",s); - print_maps(mm->mappings, " Currently defined mappings:\n"); - print_maps(mm->physavail, " Currently available physical areas:\n"); - print_maps(mm->physused, " Currently used physical areas:\n"); - print_maps(mm->virtavail, " Currently available virtual areas:\n"); - print_maps(mm->virtused, " Currently used virtual areas:\n"); - print_maps(mm->physperm, " Permanently used physical areas:\n"); - print_maps(mm->sallocphys, " Physical memory used for salloc:\n"); - print_maps(mm->sallocfree, " Memory available for salloc:\n"); - print_maps(mm->sallocused, " Memory allocated through salloc:\n"); - for (freemaps=0, free=mm->freemaps; free; freemaps++, free=free->next); - printk(" %ld free maps.\n", freemaps); -} - -void print_hash_table(void) { - struct _mm_private *mm = (struct _mm_private *) bd->mm_private; - hash_entry *p=(hash_entry *) mm->sdr1; - u_int i, valid=0; - for (i=0; i<((mm->hashmask)>>3)+8; i++) { - if (p[i].key<0) valid++; - } - printk("%u valid hash entries on pass 1.\n", valid); - valid = 0; - for (i=0; i<((mm->hashmask)>>3)+8; i++) { - if (p[i].key<0) valid++; - } - printk("%u valid hash entries on pass 2.\n" - " vpn:rpn_attr, p/s, pteg.i\n", valid); - for (i=0; i<((mm->hashmask)>>3)+8; i++) { - if (p[i].key<0) { - u_int pteg=(i>>3); - u_long vpn; - vpn = (pteg^((p[i].key)>>7)) &0x3ff; - if (p[i].key&0x40) vpn^=0x3ff; - vpn |= ((p[i].key<<9)&0xffff0000) - | ((p[i].key<<10)&0xfc00); - printk("%08lx:%08lx, %s, %5d.%d\n", - vpn, p[i].rpn, p[i].key&0x40 ? "sec" : "pri", - pteg, i%8); - } - } -} - -#endif |