summaryrefslogtreecommitdiffstats
path: root/arch/ppc/mm
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-04 07:40:19 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-04 07:40:19 +0000
commit33263fc5f9ac8e8cb2b22d06af3ce5ac1dd815e4 (patch)
tree2d1b86a40bef0958a68cf1a2eafbeb0667a70543 /arch/ppc/mm
parent216f5f51aa02f8b113aa620ebc14a9631a217a00 (diff)
Merge with Linux 2.3.32.
Diffstat (limited to 'arch/ppc/mm')
-rw-r--r--arch/ppc/mm/Makefile6
-rw-r--r--arch/ppc/mm/fault.c34
-rw-r--r--arch/ppc/mm/init.c337
-rw-r--r--arch/ppc/mm/mem_pieces.c224
-rw-r--r--arch/ppc/mm/mem_pieces.h61
5 files changed, 395 insertions, 267 deletions
diff --git a/arch/ppc/mm/Makefile b/arch/ppc/mm/Makefile
index 2b07c7227..43f53ef4d 100644
--- a/arch/ppc/mm/Makefile
+++ b/arch/ppc/mm/Makefile
@@ -8,6 +8,10 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := mm.o
-O_OBJS = fault.o init.o extable.o
+O_OBJS = fault.o init.o mem_pieces.o extable.o
+
+ifeq ($(CONFIG_4xx),y)
+O_OBJS += 4xx_tlb.o
+endif
include $(TOPDIR)/Rules.make
diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c
index aa4f97acd..0427f2166 100644
--- a/arch/ppc/mm/fault.c
+++ b/arch/ppc/mm/fault.c
@@ -52,36 +52,36 @@ void bad_page_fault(struct pt_regs *, unsigned long);
void do_page_fault(struct pt_regs *, unsigned long, unsigned long);
/*
- * The error_code parameter is DSISR for a data fault, SRR1 for
- * an instruction fault.
+ * For 600- and 800-family processors, the error_code parameter is DSISR
+ * for a data fault, SRR1 for an instruction fault. For 400-family processors
+ * the error_code parameter is ESR for a data fault, 0 for an instruction
+ * fault.
*/
void do_page_fault(struct pt_regs *regs, unsigned long address,
unsigned long error_code)
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
+#if defined(CONFIG_4xx)
+ int is_write = error_code & ESR_DST;
+#else
+ int is_write = error_code & 0x02000000;
+#endif /* CONFIG_4xx */
- /*printk("address: %08lx nip:%08lx code: %08lx %s%s%s%s%s%s\n",
- address,regs->nip,error_code,
- (error_code&0x40000000)?"604 tlb&htab miss ":"",
- (error_code&0x20000000)?"603 tlbmiss ":"",
- (error_code&0x02000000)?"write ":"",
- (error_code&0x08000000)?"prot ":"",
- (error_code&0x80000000)?"I/O ":"",
- (regs->trap == 0x400)?"instr":"data"
- );*/
-
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_fault_handler && regs->trap == 0x300) {
debugger_fault_handler(regs);
return;
}
+#if !defined(CONFIG_4xx)
if (error_code & 0x00400000) {
/* DABR match */
if (debugger_dabr_match(regs))
return;
}
-#endif
+#endif /* !CONFIG_4xx */
+#endif /* CONFIG_XMON || CONFIG_KGDB */
+
if (in_interrupt()) {
static int complained;
if (complained < 20) {
@@ -107,12 +107,12 @@ void do_page_fault(struct pt_regs *regs, unsigned long address,
goto bad_area;
good_area:
-#ifdef CONFIG_6xx
+#if defined(CONFIG_6xx)
if (error_code & 0x95700000)
/* an error such as lwarx to I/O controller space,
address matching DABR, eciwx, etc. */
#endif /* CONFIG_6xx */
-#ifdef CONFIG_8xx
+#if defined(CONFIG_8xx)
/* The MPC8xx seems to always set 0x80000000, which is
* "undefined". Of those that can be set, this is the only
* one which seems bad.
@@ -124,7 +124,7 @@ good_area:
/* a write */
- if (error_code & 0x02000000) {
+ if (is_write) {
if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
/* a read */
@@ -135,7 +135,7 @@ good_area:
if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto bad_area;
}
- if (!handle_mm_fault(current, vma, address, error_code & 0x02000000))
+ if (!handle_mm_fault(current, vma, address, is_write))
goto bad_area;
up(&mm->mmap_sem);
/*
diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c
index 025145d71..397083aca 100644
--- a/arch/ppc/mm/init.c
+++ b/arch/ppc/mm/init.c
@@ -40,6 +40,7 @@
#include <linux/blk.h> /* for initrd_* */
#endif
+#include <asm/pgalloc.h>
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
@@ -56,6 +57,10 @@
#include <asm/amigahw.h>
#include <asm/gemini.h>
+#include "mem_pieces.h"
+
+#define PGTOKB(pages) (((pages) * PAGE_SIZE) >> 10)
+
int prom_trashed;
atomic_t next_mmu_context;
unsigned long *end_of_DRAM;
@@ -71,7 +76,6 @@ extern char __prep_begin, __prep_end;
extern char __pmac_begin, __pmac_end;
extern char __apus_begin, __apus_end;
extern char __openfirmware_begin, __openfirmware_end;
-char *klimit = _end;
struct device_node *memory_node;
unsigned long ioremap_base;
unsigned long ioremap_bot;
@@ -98,34 +102,13 @@ void map_page(unsigned long va, unsigned long pa, int flags);
extern void die_if_kernel(char *,struct pt_regs *,long);
extern void show_net_buffers(void);
-
-/*
- * The following stuff defines a data structure for representing
- * areas of memory as an array of (address, length) pairs, and
- * procedures for manipulating them.
- */
-#define MAX_MEM_REGIONS 32
-
-struct mem_pieces {
- int n_regions;
- struct reg_property regions[MAX_MEM_REGIONS];
-};
struct mem_pieces phys_mem;
-struct mem_pieces phys_avail;
-
-static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int);
-static void set_phys_avail(void);
-void *find_mem_piece(unsigned, unsigned);
-static void print_mem_pieces(struct mem_pieces *);
-#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
-static void append_mem_piece(struct mem_pieces *, unsigned, unsigned);
-#endif
extern struct task_struct *current_set[NR_CPUS];
PTE *Hash, *Hash_end;
unsigned long Hash_size, Hash_mask;
-#ifndef CONFIG_8xx
+#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx)
#ifdef CONFIG_PPC64
unsigned long long _SDR1;
#else
@@ -174,12 +157,10 @@ static inline unsigned long p_mapped_by_bats(unsigned long pa)
return 0;
}
-#else /* CONFIG_8xx */
-
-/* 8xx doesn't have BATs */
+#else /* CONFIG_4xx || CONFIG_8xx */
#define v_mapped_by_bats(x) (0UL)
#define p_mapped_by_bats(x) (0UL)
-#endif /* CONFIG_8xx */
+#endif /* !CONFIG_4xx && !CONFIG_8xx */
/*
* this tells the system to map all of ram with the segregs
@@ -343,24 +324,23 @@ void show_mem(void)
void si_meminfo(struct sysinfo *val)
{
- int i;
+ int i, c;
i = max_mapnr;
- val->totalram = 0;
- val->sharedram = 0;
+ val->totalram = totalram_pages;
val->freeram = nr_free_pages();
val->bufferram = atomic_read(&buffermem_pages);
+ val->sharedram = 0;
while (i-- > 0) {
if (PageReserved(mem_map+i))
continue;
- val->totalram++;
- if (!atomic_read(&mem_map[i].count))
- continue;
- val->sharedram += atomic_read(&mem_map[i].count) - 1;
+ c = atomic_read(&mem_map[i].count);
+ if (c > 1)
+ val->sharedram += c - 1;
}
- val->totalram <<= PAGE_SHIFT;
- val->sharedram <<= PAGE_SHIFT;
- return;
+ val->totalhigh = 0;
+ val->freehigh = 0;
+ val->mem_unit = PAGE_SIZE;
}
void *
@@ -422,7 +402,7 @@ __ioremap(unsigned long addr, unsigned long size, unsigned long flags)
if (mem_init_done) {
struct vm_struct *area;
- area = get_vm_area(size);
+ area = get_vm_area(size, VM_IOREMAP);
if (area == 0)
return NULL;
v = VMALLOC_VMADDR(area->addr);
@@ -593,191 +573,8 @@ mmu_context_overflow(void)
}
#endif /* CONFIG_8xx */
-/*
- * Set phys_avail to phys_mem less the kernel text/data/bss.
- */
-static void __init set_phys_avail(void)
-{
- unsigned long kstart, ksize;
-
- /* we can't call the prom any more at this stage, so
- all of memory is available (after klimit) */
- phys_avail = phys_mem;
-
- /*
- * phys_avail records memory we can use.
- * Make sure the kernel text/data/bss is not in it.
- */
- kstart = __pa(_stext); /* should be 0 */
- ksize = PAGE_ALIGN(klimit - _stext);
- remove_mem_piece(&phys_avail, kstart, ksize, 0);
- remove_mem_piece(&phys_avail, 0, 0x4000, 0);
-
-#ifdef CONFIG_BLK_DEV_INITRD
- if (initrd_start) {
- /*
- * Remove the initialized ramdisk from the available memory.
- */
- remove_mem_piece(&phys_avail, __pa(initrd_start),
- initrd_end - initrd_start, 1);
- }
-#endif /* CONFIG_BLK_DEV_INITRD */
-}
-
-/*
- * Scan a region for a piece of a given size with the required alignment.
- */
-void __init *find_mem_piece(unsigned size, unsigned align)
-{
- int i;
- unsigned a, e;
- struct mem_pieces *mp = &phys_avail;
-
- for (i = 0; i < mp->n_regions; ++i) {
- a = mp->regions[i].address;
- e = a + mp->regions[i].size;
- a = (a + align - 1) & -align;
- if (a + size <= e) {
- remove_mem_piece(mp, a, size, 1);
- return __va(a);
- }
- }
- printk("Couldn't find %u bytes at %u alignment\n", size, align);
- abort();
- return NULL;
-}
-
-/*
- * Remove some memory from an array of pieces
- */
-static void __init
-remove_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size,
- int must_exist)
-{
- int i, j;
- unsigned end, rs, re;
- struct reg_property *rp;
-
- end = start + size;
- for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) {
- if (end > rp->address && start < rp->address + rp->size)
- break;
- }
- if (i >= mp->n_regions) {
- if (must_exist)
- printk("remove_mem_piece: [%x,%x) not in any region\n",
- start, end);
- return;
- }
- for (; i < mp->n_regions && end > rp->address; ++i, ++rp) {
- rs = rp->address;
- re = rs + rp->size;
- if (must_exist && (start < rs || end > re)) {
- printk("remove_mem_piece: bad overlap [%x,%x) with",
- start, end);
- print_mem_pieces(mp);
- must_exist = 0;
- }
- if (start > rs) {
- rp->size = start - rs;
- if (end < re) {
- /* need to split this entry */
- if (mp->n_regions >= MAX_MEM_REGIONS)
- panic("eek... mem_pieces overflow");
- for (j = mp->n_regions; j > i + 1; --j)
- mp->regions[j] = mp->regions[j-1];
- ++mp->n_regions;
- rp[1].address = end;
- rp[1].size = re - end;
- }
- } else {
- if (end < re) {
- rp->address = end;
- rp->size = re - end;
- } else {
- /* need to delete this entry */
- for (j = i; j < mp->n_regions - 1; ++j)
- mp->regions[j] = mp->regions[j+1];
- --mp->n_regions;
- --i;
- --rp;
- }
- }
- }
-}
-
-static void __init print_mem_pieces(struct mem_pieces *mp)
-{
- int i;
-
- for (i = 0; i < mp->n_regions; ++i)
- printk(" [%x, %x)", mp->regions[i].address,
- mp->regions[i].address + mp->regions[i].size);
- printk("\n");
-}
-
-#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
-/*
- * Add some memory to an array of pieces
- */
-static void __init
-append_mem_piece(struct mem_pieces *mp, unsigned start, unsigned size)
-{
- struct reg_property *rp;
-
- if (mp->n_regions >= MAX_MEM_REGIONS)
- return;
- rp = &mp->regions[mp->n_regions++];
- rp->address = start;
- rp->size = size;
-}
-#endif
-
-#ifndef CONFIG_8xx
-static void hash_init(void);
+#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx)
static void get_mem_prop(char *, struct mem_pieces *);
-static void sort_mem_pieces(struct mem_pieces *);
-static void coalesce_mem_pieces(struct mem_pieces *);
-
-static void __init sort_mem_pieces(struct mem_pieces *mp)
-{
- unsigned long a, s;
- int i, j;
-
- for (i = 1; i < mp->n_regions; ++i) {
- a = mp->regions[i].address;
- s = mp->regions[i].size;
- for (j = i - 1; j >= 0; --j) {
- if (a >= mp->regions[j].address)
- break;
- mp->regions[j+1] = mp->regions[j];
- }
- mp->regions[j+1].address = a;
- mp->regions[j+1].size = s;
- }
-}
-
-static void __init coalesce_mem_pieces(struct mem_pieces *mp)
-{
- unsigned long a, s, ns;
- int i, j, d;
-
- d = 0;
- for (i = 0; i < mp->n_regions; i = j) {
- a = mp->regions[i].address;
- s = mp->regions[i].size;
- for (j = i + 1; j < mp->n_regions
- && mp->regions[j].address - a <= s; ++j) {
- ns = mp->regions[j].address + mp->regions[j].size - a;
- if (ns > s)
- s = ns;
- }
- mp->regions[d].address = a;
- mp->regions[d].size = s;
- ++d;
- }
- mp->n_regions = d;
-}
#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC)
/*
@@ -799,8 +596,8 @@ static void __init get_mem_prop(char *name, struct mem_pieces *mp)
memcpy(mp->regions, rp, s);
/* Make sure the pieces are sorted. */
- sort_mem_pieces(mp);
- coalesce_mem_pieces(mp);
+ mem_pieces_sort(mp);
+ mem_pieces_coalesce(mp);
}
#endif /* CONFIG_PMAC || CONFIG_CHRP || CONFIG_ALL_PPC */
@@ -918,7 +715,7 @@ static void __init mapin_ram(void)
f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE;
#ifndef CONFIG_8xx
else
- /* On the powerpc (not 8xx), no user access
+ /* On the powerpc, denying user access
forces R/W kernel access */
f |= _PAGE_USER;
#endif /* CONFIG_8xx */
@@ -939,7 +736,7 @@ static void __init *MMU_get_page(void)
} else if (init_bootmem_done) {
p = alloc_bootmem_pages(PAGE_SIZE);
} else {
- p = find_mem_piece(PAGE_SIZE, PAGE_SIZE);
+ p = mem_pieces_find(PAGE_SIZE, PAGE_SIZE);
}
if (p == 0)
panic("couldn't get a page in MMU_get_page");
@@ -1000,18 +797,32 @@ void __init free_initmem(void)
num_openfirmware_pages );
printk ("Freeing unused kernel memory: %ldk init",
- (num_freed_pages * PAGE_SIZE) >> 10);
+ PGTOKB(num_freed_pages));
+
if ( num_prep_pages )
- printk(" %ldk prep",(num_prep_pages*PAGE_SIZE)>>10);
+ printk(" %ldk prep", PGTOKB(num_prep_pages));
if ( num_pmac_pages )
- printk(" %ldk pmac",(num_pmac_pages*PAGE_SIZE)>>10);
+ printk(" %ldk pmac", PGTOKB(num_pmac_pages));
if ( num_openfirmware_pages )
- printk(" %ldk open firmware",(num_openfirmware_pages*PAGE_SIZE)>>10);
+ printk(" %ldk open firmware", PGTOKB(num_openfirmware_pages));
if ( num_apus_pages )
- printk(" %ldk apus",(num_apus_pages*PAGE_SIZE)>>10);
+ printk(" %ldk apus", PGTOKB(num_apus_pages));
printk("\n");
}
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ for (; start < end; start += PAGE_SIZE) {
+ ClearPageReserved(mem_map + MAP_NR(start));
+ set_page_count(mem_map+MAP_NR(start), 1);
+ free_page(start);
+ totalram_pages++;
+ }
+ printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+}
+#endif
+
/*
* Do very early mm setup such as finding the size of memory
* and setting up the hash table.
@@ -1063,7 +874,7 @@ void __init MMU_init(void)
setbat(3, 0x90000000, 0x90000000, 0x10000000, IO_PAGE);
break;
case _MACH_Pmac:
-#if 0
+#if 1
{
unsigned long base = 0xf3000000;
struct device_node *macio = find_devices("mac-io");
@@ -1155,7 +966,8 @@ void __init do_init_bootmem(void)
__pa(end_of_DRAM) >> PAGE_SHIFT);
/* remove the bootmem bitmap from the available memory */
- remove_mem_piece(&phys_avail, start, boot_mapsize, 1);
+ mem_pieces_remove(&phys_avail, start, boot_mapsize, 1);
+
/* add everything in phys_avail into the bootmem map */
for (i = 0; i < phys_avail.n_regions; ++i)
free_bootmem(phys_avail.regions[i].address,
@@ -1215,6 +1027,24 @@ void __init paging_init(void)
*/
empty_bad_page = alloc_bootmem_pages(PAGE_SIZE);
empty_bad_page_table = alloc_bootmem_pages(PAGE_SIZE);
+ {
+ unsigned int zones_size[MAX_NR_ZONES], i;
+ /*
+ * All pages are DMA-able so this is wrong - the zone code is
+ * assuming both regions have a value so this is necessary for
+ * now.
+ * -- Cort
+ */
+#if 1
+ for ( i = 1; i < MAX_NR_ZONES; i++ )
+ zones_size[i] = 1<<MAX_ORDER;
+ zones_size[0] = (virt_to_phys(end_of_DRAM) >> PAGE_SHIFT) -
+ ((MAX_NR_ZONES-1)*(1<<MAX_ORDER));
+#else
+ zones_size[0] = virt_to_phys(end_of_DRAM) >> PAGE_SHIFT;
+#endif
+ free_area_init(zones_size);
+ }
}
void __init mem_init(void)
@@ -1223,8 +1053,9 @@ void __init mem_init(void)
int codepages = 0;
int datapages = 0;
int initpages = 0;
+#if defined(CONFIG_CHRP) || defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC)
extern unsigned int rtas_data, rtas_size;
-
+#endif /* CONFIG_CHRP || CONFIG_PMAC || CONFIG_ALL_PPC */
max_mapnr = max_low_pfn;
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
num_physpages = max_mapnr; /* RAM is assumed contiguous */
@@ -1239,6 +1070,14 @@ void __init mem_init(void)
clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags);
}
#endif /* CONFIG_BLK_DEV_INITRD */
+
+#if defined(CONFIG_CHRP) || defined(CONFIG_PMAC) || defined(CONFIG_ALL_PPC)
+ /* mark the RTAS pages as reserved */
+ if ( rtas_data )
+ for (addr = rtas_data; addr < PAGE_ALIGN(rtas_data+rtas_size) ;
+ addr += PAGE_SIZE)
+ SetPageReserved(mem_map + MAP_NR(addr));
+#endif /* CONFIG_CHRP || CONFIG_PMAC || CONFIG_ALL_PPC */
for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM;
addr += PAGE_SIZE) {
@@ -1249,12 +1088,12 @@ void __init mem_init(void)
else if (addr >= (unsigned long)&__init_begin
&& addr < (unsigned long)&__init_end)
initpages++;
- else if (addr < (ulong) klimit)
+ else
datapages++;
}
printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n",
- (unsigned long) nr_free_pages << (PAGE_SHIFT-10),
+ (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
codepages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10),
@@ -1262,7 +1101,7 @@ void __init mem_init(void)
mem_init_done = 1;
}
-#ifndef CONFIG_8xx
+#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx)
#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP) || defined(CONFIG_ALL_PPC)
/*
* On systems with Open Firmware, collect information about
@@ -1324,7 +1163,7 @@ unsigned long __init *pmac_find_end_of_memory(void)
phys_mem.n_regions = 1;
}
- set_phys_avail();
+ set_phys_avail(&phys_mem);
#undef RAM_LIMIT
return __va(total);
@@ -1353,8 +1192,8 @@ unsigned long __init *prep_find_end_of_memory(void)
total = 0x02000000;
printk("Ramsize default to be %ldM\n", total>>20);
}
- append_mem_piece(&phys_mem, 0, total);
- set_phys_avail();
+ mem_pieces_append(&phys_mem, 0, total);
+ set_phys_avail(&phys_mem);
return (__va(total));
}
@@ -1376,7 +1215,7 @@ unsigned long __init *gemini_find_end_of_memory(void)
phys_mem.n_regions = 1;
ret = __va(phys_mem.regions[0].size);
- set_phys_avail();
+ set_phys_avail(&phys_mem);
return ret;
}
#endif /* defined(CONFIG_GEMINI) */
@@ -1412,8 +1251,8 @@ unsigned long __init *apus_find_end_of_memory(void)
}
/* Now register the memory block. */
- append_mem_piece(&phys_mem, memory[0].addr, memory[0].size);
- set_phys_avail();
+ mem_pieces_append(&phys_mem, memory[0].addr, memory[0].size);
+ set_phys_avail(&phys_mem);
/* Remove the memory chunks that are controlled by special
Phase5 hardware. */
@@ -1427,8 +1266,8 @@ unsigned long __init *apus_find_end_of_memory(void)
if (shadow)
{
top -= HARDWARE_MAPPED_SIZE;
- remove_mem_piece(&phys_avail, top,
- HARDWARE_MAPPED_SIZE, 0);
+ mem_pieces_remove(&phys_avail, top,
+ HARDWARE_MAPPED_SIZE, 0);
}
/* Remove the upper 512KB where the PPC exception
@@ -1436,9 +1275,9 @@ unsigned long __init *apus_find_end_of_memory(void)
top -= HARDWARE_MAPPED_SIZE;
#if 0
/* This would be neat, but it breaks on A3000 machines!? */
- remove_mem_piece(&phys_avail, top, 16384, 0);
+ mem_pieces_remove(&phys_avail, top, 16384, 0);
#else
- remove_mem_piece(&phys_avail, top, HARDWARE_MAPPED_SIZE, 0);
+ mem_pieces_remove(&phys_avail, top, HARDWARE_MAPPED_SIZE, 0);
#endif
}
@@ -1497,7 +1336,7 @@ static void __init hash_init(void)
if ( ppc_md.progress ) ppc_md.progress("hash:find piece", 0x322);
/* Find some memory for the hash table. */
if ( Hash_size )
- Hash = find_mem_piece(Hash_size, Hash_size);
+ Hash = mem_pieces_find(Hash_size, Hash_size);
else
Hash = 0;
@@ -1575,7 +1414,7 @@ unsigned long __init *m8xx_find_end_of_memory(void)
ret = __va(phys_mem.regions[0].address+
phys_mem.regions[0].size);
- set_phys_avail();
+ set_phys_avail(&phys_mem);
return ret;
}
#endif /* ndef CONFIG_8xx */
diff --git a/arch/ppc/mm/mem_pieces.c b/arch/ppc/mm/mem_pieces.c
new file mode 100644
index 000000000..138b2a40f
--- /dev/null
+++ b/arch/ppc/mm/mem_pieces.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
+ * Changes to accomodate Power Macintoshes.
+ * Cort Dougan <cort@cs.nmt.edu>
+ * Rewrites.
+ * Grant Erickson <grant@lcse.umn.edu>
+ * General rework and split from mm/init.c.
+ *
+ * Module name: mem_pieces.c
+ *
+ * Description:
+ * Routines and data structures for manipulating and representing
+ * phyiscal memory extents (i.e. address/length pairs).
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/blk.h>
+
+#include <asm/page.h>
+#include <asm/prom.h>
+
+#include "mem_pieces.h"
+
+extern char _start[], _end[];
+extern char _stext[], etext[];
+
+char *klimit = _end;
+
+struct mem_pieces phys_avail;
+
+static void mem_pieces_print(struct mem_pieces *);
+
+/*
+ * Scan a region for a piece of a given size with the required alignment.
+ */
+void __init *
+mem_pieces_find(unsigned int size, unsigned int align)
+{
+ int i;
+ unsigned a, e;
+ struct mem_pieces *mp = &phys_avail;
+
+ for (i = 0; i < mp->n_regions; ++i) {
+ a = mp->regions[i].address;
+ e = a + mp->regions[i].size;
+ a = (a + align - 1) & -align;
+ if (a + size <= e) {
+ mem_pieces_remove(mp, a, size, 1);
+ return __va(a);
+ }
+ }
+ panic("Couldn't find %u bytes at %u alignment\n", size, align);
+
+ return NULL;
+}
+
+/*
+ * Remove some memory from an array of pieces
+ */
+void __init
+mem_pieces_remove(struct mem_pieces *mp, unsigned int start, unsigned int size,
+ int must_exist)
+{
+ int i, j;
+ unsigned int end, rs, re;
+ struct reg_property *rp;
+
+ end = start + size;
+ for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) {
+ if (end > rp->address && start < rp->address + rp->size)
+ break;
+ }
+ if (i >= mp->n_regions) {
+ if (must_exist)
+ printk("mem_pieces_remove: [%x,%x) not in any region\n",
+ start, end);
+ return;
+ }
+ for (; i < mp->n_regions && end > rp->address; ++i, ++rp) {
+ rs = rp->address;
+ re = rs + rp->size;
+ if (must_exist && (start < rs || end > re)) {
+ printk("mem_pieces_remove: bad overlap [%x,%x) with",
+ start, end);
+ mem_pieces_print(mp);
+ must_exist = 0;
+ }
+ if (start > rs) {
+ rp->size = start - rs;
+ if (end < re) {
+ /* need to split this entry */
+ if (mp->n_regions >= MEM_PIECES_MAX)
+ panic("eek... mem_pieces overflow");
+ for (j = mp->n_regions; j > i + 1; --j)
+ mp->regions[j] = mp->regions[j-1];
+ ++mp->n_regions;
+ rp[1].address = end;
+ rp[1].size = re - end;
+ }
+ } else {
+ if (end < re) {
+ rp->address = end;
+ rp->size = re - end;
+ } else {
+ /* need to delete this entry */
+ for (j = i; j < mp->n_regions - 1; ++j)
+ mp->regions[j] = mp->regions[j+1];
+ --mp->n_regions;
+ --i;
+ --rp;
+ }
+ }
+ }
+}
+
+static void __init
+mem_pieces_print(struct mem_pieces *mp)
+{
+ int i;
+
+ for (i = 0; i < mp->n_regions; ++i)
+ printk(" [%x, %x)", mp->regions[i].address,
+ mp->regions[i].address + mp->regions[i].size);
+ printk("\n");
+}
+
+#if defined(CONFIG_PREP) || defined(CONFIG_APUS) || defined(CONFIG_ALL_PPC)
+/*
+ * Add some memory to an array of pieces
+ */
+void __init
+mem_pieces_append(struct mem_pieces *mp, unsigned int start, unsigned int size)
+{
+ struct reg_property *rp;
+
+ if (mp->n_regions >= MEM_PIECES_MAX)
+ return;
+ rp = &mp->regions[mp->n_regions++];
+ rp->address = start;
+ rp->size = size;
+}
+#endif
+
+void __init
+mem_pieces_sort(struct mem_pieces *mp)
+{
+ unsigned long a, s;
+ int i, j;
+
+ for (i = 1; i < mp->n_regions; ++i) {
+ a = mp->regions[i].address;
+ s = mp->regions[i].size;
+ for (j = i - 1; j >= 0; --j) {
+ if (a >= mp->regions[j].address)
+ break;
+ mp->regions[j+1] = mp->regions[j];
+ }
+ mp->regions[j+1].address = a;
+ mp->regions[j+1].size = s;
+ }
+}
+
+void __init
+mem_pieces_coalesce(struct mem_pieces *mp)
+{
+ unsigned long a, s, ns;
+ int i, j, d;
+
+ d = 0;
+ for (i = 0; i < mp->n_regions; i = j) {
+ a = mp->regions[i].address;
+ s = mp->regions[i].size;
+ for (j = i + 1; j < mp->n_regions
+ && mp->regions[j].address - a <= s; ++j) {
+ ns = mp->regions[j].address + mp->regions[j].size - a;
+ if (ns > s)
+ s = ns;
+ }
+ mp->regions[d].address = a;
+ mp->regions[d].size = s;
+ ++d;
+ }
+ mp->n_regions = d;
+}
+
+/*
+ * Set phys_avail to phys_mem less the kernel text/data/bss.
+ */
+void __init
+set_phys_avail(struct mem_pieces *mp)
+{
+ unsigned long kstart, ksize;
+
+ /*
+ * Initially, available phyiscal memory is equivalent to all
+ * physical memory.
+ */
+
+ phys_avail = *mp;
+
+ /*
+ * Map out the kernel text/data/bss from the available physical
+ * memory.
+ */
+
+ kstart = __pa(_stext); /* should be 0 */
+ ksize = PAGE_ALIGN(klimit - _stext);
+
+ printk("kstart = 0x%08lx, ksize = 0x%08lx\n", kstart, ksize);
+
+ mem_pieces_remove(&phys_avail, kstart, ksize, 0);
+ mem_pieces_remove(&phys_avail, 0, 0x4000, 0);
+
+#if defined(CONFIG_BLK_DEV_INITRD)
+ /* Remove the init RAM disk from the available memory. */
+ if (initrd_start) {
+ mem_pieces_remove(&phys_avail, __pa(initrd_start),
+ initrd_end - initrd_start, 1);
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+}
diff --git a/arch/ppc/mm/mem_pieces.h b/arch/ppc/mm/mem_pieces.h
new file mode 100644
index 000000000..6dbe045da
--- /dev/null
+++ b/arch/ppc/mm/mem_pieces.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
+ * Changes to accomodate Power Macintoshes.
+ * Cort Dougan <cort@cs.nmt.edu>
+ * Rewrites.
+ * Grant Erickson <grant@lcse.umn.edu>
+ * General rework and split from mm/init.c.
+ *
+ * Module name: mem_pieces.h
+ *
+ * Description:
+ * Routines and data structures for manipulating and representing
+ * phyiscal memory extents (i.e. address/length pairs).
+ *
+ */
+
+#ifndef __MEM_PIECES_H__
+#define __MEM_PIECES_H__
+
+#include <linux/init.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Type Definitions */
+
+#define MEM_PIECES_MAX 32
+
+struct mem_pieces {
+ int n_regions;
+ struct reg_property regions[MEM_PIECES_MAX];
+};
+
+
+/* Global Variables */
+
+extern char *klimit;
+extern struct mem_pieces phys_avail;
+
+
+/* Function Prototypes */
+
+extern void *mem_pieces_find(unsigned int size, unsigned int align);
+extern void mem_pieces_remove(struct mem_pieces *mp, unsigned int start,
+ unsigned int size, int must_exist);
+extern void mem_pieces_append(struct mem_pieces *mp, unsigned int start,
+ unsigned int size);
+extern void mem_pieces_coalesce(struct mem_pieces *mp);
+extern void mem_pieces_sort(struct mem_pieces *mp);
+
+extern void set_phys_avail(struct mem_pieces *mp);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MEM_PIECES_H__ */