/*
* 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 the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
/* 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 <rtems/bspIo.h>
#include <sys/types.h>
#include <libcpu/spr.h>
#include "bootldr.h"
#include <libcpu/mmu.h>
#include <libcpu/page.h>
#include <limits.h>
extern void (tlb_handlers)(void);
extern void (_handler_glue)(void);
/* 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(PPC_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_PPC_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=%d, dsisr=%lx, faultaddr =%lx, "
"msr=%lx opcode=%x\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 %p = %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.
*/
static 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;
if ( residual_fw_is_qemu( res ) ) {
/* save command-line - QEMU firmware sets R6/R7 to
* commandline start/end (NON-PReP STD)
*/
int len = bd->r7 - bd->r6;
if ( len > 0 ) {
if ( len > sizeof(bd->cmd_line) - 1 )
len = sizeof(bd->cmd_line) - 1;
codemove(bd->cmd_line, bd->r6, len, bd->cache_lsize);
bd->cmd_line[len] = 0;
}
}
/* 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;
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