diff options
author | Kanoj Sarcar <kanoj@engr.sgi.com> | 2000-06-06 03:29:39 +0000 |
---|---|---|
committer | Kanoj Sarcar <kanoj@engr.sgi.com> | 2000-06-06 03:29:39 +0000 |
commit | 4ff3c0a835b12eec3d76c3c145ed243840ddca94 (patch) | |
tree | 1044fadf8a5c468159831970ed28fc27af64b411 /arch/mips64/kernel/head.S | |
parent | 448ade02e6a7fb60bc6e7f1756e6c45bff6bb038 (diff) |
Initial rudimentary support for mapped kernel. The kernel text and data
are placed in the cksseg area, and such behavior is tuned thru a make
option (off by default). Basis for future kernel text replication and
partitioning work/study.
Diffstat (limited to 'arch/mips64/kernel/head.S')
-rw-r--r-- | arch/mips64/kernel/head.S | 455 |
1 files changed, 368 insertions, 87 deletions
diff --git a/arch/mips64/kernel/head.S b/arch/mips64/kernel/head.S index 0615209f9..b2c006886 100644 --- a/arch/mips64/kernel/head.S +++ b/arch/mips64/kernel/head.S @@ -1,107 +1,388 @@ -/* $Id: head.S,v 1.6 2000/03/27 21:05:04 ulfc Exp $ +/* $Id: ip27-memory.c,v 1.2 2000/01/27 01:05:24 ralf Exp $ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Head.S contains the MIPS exception handler and startup code. + * Copyright (C) 2000 by Ralf Baechle + * Copyright (C) 2000 by Silicon Graphics, Inc. * - * Copyright (C) 1994, 1995 Waldorf Electronics - * Written by Ralf Baechle and Andreas Busse - * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999 Ralf Baechle - * Copyright (C) 1999 Silicon Graphics, Inc. + * On SGI IP27 the ARC memory configuration data is completly bogus but + * alternate easier to use mechanisms are available. */ -#define __ASSEMBLY__ -#include <linux/config.h> #include <linux/init.h> -#include <asm/asm.h> -#include <asm/regdef.h> -#include <asm/processor.h> -#include <asm/mipsregs.h> -#include <asm/stackframe.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/swap.h> + +#include <asm/page.h> +#include <asm/bootinfo.h> +#include <asm/addrspace.h> +#include <asm/pgtable.h> +#include <asm/sn/types.h> +#include <asm/sn/addrs.h> +#include <asm/sn/klconfig.h> +#include <asm/sn/arch.h> +#include <asm/mmzone.h> + +typedef unsigned long pfn_t; /* into <asm/sn/types.h> */ +#define KDM_TO_PHYS(x) ((x) & TO_PHYS_MASK) /* into asm/addrspace.h */ + +extern char _end; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define SLOT_IGNORED 0xffff - .text +short slot_lastfilled_cache[MAX_COMPACT_NODES]; +unsigned short slot_psize_cache[MAX_COMPACT_NODES][MAX_MEM_SLOTS]; +static pfn_t numpages; +static pfn_t pagenr = 0; -EXPORT(stext) # used for profiling -EXPORT(_stext) +plat_pg_data_t *plat_node_data[MAX_COMPACT_NODES]; +bootmem_data_t plat_node_bdata[MAX_COMPACT_NODES]; - __INIT +int numa_debug(void) +{ + printk("NUMA debug\n"); + *(int *)0 = 0; + return(0); +} -NESTED(kernel_entry, 16, sp) # kernel entry point +/* + * Return pfn of first free page of memory on a node. PROM may allocate + * data structures on the first couple of pages of the first slot of each + * node. If this is the case, getfirstfree(node) > getslotstart(node, 0). + */ +pfn_t node_getfirstfree(cnodeid_t cnode) +{ + unsigned long loadbase = CKSEG0; + nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); -#ifdef CONFIG_ARC64 - la t0, 1f - jr t0 -1: +#ifdef CONFIG_MAPPED_KERNEL + loadbase = CKSSEG; #endif + if (cnode == 0) + return (KDM_TO_PHYS(PAGE_ALIGN((unsigned long)(&_end)) - + (loadbase - K0BASE)) >> PAGE_SHIFT); + return (KDM_TO_PHYS(PAGE_ALIGN(SYMMON_STK_ADDR(nasid, 0))) >> PAGE_SHIFT); +} - ori sp, 0xf # align stack on 16 byte. - xori sp, 0xf +/* + * Return the number of pages of memory provided by the given slot + * on the specified node. + */ +pfn_t slot_getsize(cnodeid_t node, int slot) +{ + return (pfn_t) slot_psize_cache[node][slot]; +} - /* Note that all firmware passed argument registers still - have their values. */ - jal prom_init # initialize firmware +/* + * Return highest slot filled + */ +int node_getlastslot(cnodeid_t node) +{ + return (int) slot_lastfilled_cache[node]; +} - CLI # disable interrupts +/* + * Return the pfn of the last free page of memory on a node. + */ +pfn_t node_getmaxclick(cnodeid_t node) +{ + pfn_t slot_psize; + int slot; - mfc0 t0, CP0_STATUS /* - * On IP27, I am seeing the TS bit set when the - * kernel is loaded. Maybe because the kernel is - * in ckseg0 and not xkphys? Clear it anyway ... + * Start at the top slot. When we find a slot with memory in it, + * that's the winner. */ - li t1, ~(ST0_TS|ST0_CU1|ST0_CU2|ST0_CU3) - and t0, t1 - or t0, (ST0_CU0|ST0_KX|ST0_SX|ST0_FR) # Bogosity: cu0 indicates kernel - mtc0 t0, CP0_STATUS # thread in copy_thread. - - la $28, init_task_union # init current pointer - daddiu t0, $28, KERNEL_STACK_SIZE-32 - sd t0, kernelsp - dsubu sp, t0, 4*SZREG # init stack pointer - move t0, $28 -#ifdef CONFIG_SMP - mtc0 t0, CP0_WATCHLO - dsrl32 t0, t0, 0 - mtc0 t0, CP0_WATCHHI -#endif - jal start_kernel -1: b 1b # just in case ... - END(kernel_entry) - -NESTED(bootstrap, 16, sp) -#ifdef CONFIG_ARC64 - la t0, 1f - jr t0 -1: -#endif - CLI - mfc0 t0, CP0_STATUS - li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3) - and t0, t1 - or t0, (ST0_CU0|ST0_KX|ST0_SX|ST0_FR) # Bogosity: cu0 indicates kernel - mtc0 t0, CP0_STATUS # thread in copy_thread. - jal cboot - END(bootstrap) - - __FINIT - - .comm kernelsp, 8, 8 # current stackpointer - -#define PAGE_SIZE 0x1000 - - .macro page name, order=0 - .globl \name - .org . + (PAGE_SIZE << \order) -\name: .size \name, (PAGE_SIZE << \order) - .type \name, @object - .endm - - .align 12 - .data - - page swapper_pg_dir, 1 - page invalid_pte_table, 1 - page invalid_pmd_table, 1 - page empty_bad_page_table, 1 - page empty_bad_page + for (slot = (node_getnumslots(node) - 1); slot >= 0; slot--) { + if ((slot_psize = slot_getsize(node, slot))) { + if (slot_psize == SLOT_IGNORED) + continue; + /* Return the basepfn + the slot size, minus 1. */ + return slot_getbasepfn(node, slot) + slot_psize - 1; + } + } + + /* + * If there's no memory on the node, return 0. This is likely + * to cause problems. + */ + return (pfn_t)0; +} + +static pfn_t slot_psize_compute(cnodeid_t node, int slot) +{ + nasid_t nasid; + lboard_t *brd; + klmembnk_t *banks; + unsigned long size; + + nasid = COMPACT_TO_NASID_NODEID(node); + /* Find the node board */ + brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27); + if (!brd) + return 0; + + /* Get the memory bank structure */ + banks = (klmembnk_t *)find_first_component(brd, KLSTRUCT_MEMBNK); + if (!banks) + return 0; + + /* Size in _Megabytes_ */ + size = (unsigned long)banks->membnk_bnksz[slot/4]; + + /* hack for 128 dimm banks */ + if (size <= 128) { + if (slot%4 == 0) { + size <<= 20; /* size in bytes */ + return(size >> PAGE_SHIFT); + } else { + return 0; + } + } else { + size /= 4; + size <<= 20; + return(size >> PAGE_SHIFT); + } +} + +pfn_t szmem(pfn_t fpage, pfn_t maxpmem) +{ + cnodeid_t node; + int slot, numslots; + pfn_t num_pages = 0, slot_psize; + pfn_t slot0sz = 0, nodebytes; /* Hack to detect problem configs */ + int ignore; + + for (node = 0; node < numnodes; node++) { + numslots = node_getnumslots(node); + ignore = nodebytes = 0; + for (slot = 0; slot < numslots; slot++) { + slot_psize = slot_psize_compute(node, slot); + if (slot == 0) slot0sz = slot_psize; + /* + * We need to refine the hack when we have replicated + * kernel text. + */ + nodebytes += SLOT_SIZE; + if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) > + (slot0sz << PAGE_SHIFT)) + ignore = 1; + if (ignore && slot_psize) { + printk("Ignoring slot %d onwards on node %d\n", + slot, node); + slot_psize_cache[node][slot] = SLOT_IGNORED; + slot = numslots; + continue; + } + num_pages += slot_psize; + slot_psize_cache[node][slot] = + (unsigned short) slot_psize; + if (slot_psize) + slot_lastfilled_cache[node] = slot; + } + } + if (maxpmem) + return((maxpmem > num_pages) ? num_pages : maxpmem); + else + return num_pages; +} + +/* + * HACK ALERT - Things do not work if this is not here. Maybe this is + * acting as a pseudocacheflush operation. The write pattern seems to + * be important, writing a 0 does not help. + */ +void setup_test(cnodeid_t node, pfn_t start, pfn_t end) +{ + unsigned long *ptr = __va(start << PAGE_SHIFT); + unsigned long size = 4 * 1024 * 1024; /* 4M L2 caches */ + + while (size) { + size -= sizeof(unsigned long); + *ptr = (0xdeadbeefbabeb000UL|node); + /* *ptr = 0; */ + ptr++; + } +} + +/* + * Currently, the intranode memory hole support assumes that each slot + * contains at least 32 MBytes of memory. We assume all bootmem data + * fits on the first slot. + */ +void __init prom_meminit(void) +{ + extern void mlreset(void); + cnodeid_t node; + pfn_t slot_firstpfn, slot_lastpfn, slot_freepfn; + unsigned long bootmap_size; + int node_datasz; + + node_datasz = PFN_UP(sizeof(plat_pg_data_t)); + mlreset(); + numpages = szmem(0, 0); + for (node = (numnodes - 1); node >= 0; node--) { + slot_firstpfn = slot_getbasepfn(node, 0); + slot_lastpfn = slot_firstpfn + slot_getsize(node, 0); + slot_freepfn = node_getfirstfree(node); + /* Foll line hack for non discontigmem; remove once discontigmem + * becomes the default. */ + max_low_pfn = (slot_lastpfn - slot_firstpfn); + if (node != 0) + setup_test(node, slot_freepfn, slot_lastpfn); + /* + * Allocate the node data structure on the node first. + */ + plat_node_data[node] = (plat_pg_data_t *)(__va(slot_freepfn \ + << PAGE_SHIFT)); + NODE_DATA(node)->bdata = plat_node_bdata + node; + slot_freepfn += node_datasz; + bootmap_size = init_bootmem_node(node, slot_freepfn, + slot_firstpfn, slot_lastpfn); + free_bootmem_node(node, slot_firstpfn << PAGE_SHIFT, + (slot_lastpfn - slot_firstpfn) << PAGE_SHIFT); + reserve_bootmem_node(node, slot_firstpfn << PAGE_SHIFT, + ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size); + } + printk("Total memory probed : 0x%lx pages\n", numpages); +} + +int __init page_is_ram(unsigned long pagenr) +{ + return 1; +} + +void __init +prom_free_prom_memory (void) +{ + /* We got nothing to free here ... */ +} + +#ifdef CONFIG_DISCONTIGMEM + +void __init paging_init(void) +{ + cnodeid_t node; + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + + /* Initialize the entire pgd. */ + pgd_init((unsigned long)swapper_pg_dir); + pmd_init((unsigned long)invalid_pmd_table); + memset((void *)invalid_pte_table, 0, sizeof(pte_t) * 2 * PTRS_PER_PTE); + + for (node = 0; node < numnodes; node++) { + pfn_t start_pfn = slot_getbasepfn(node, 0); + pfn_t end_pfn = node_getmaxclick(node); + + zones_size[ZONE_DMA] = end_pfn + 1 - start_pfn; + free_area_init_node(node, NODE_DATA(node), zones_size, + start_pfn << PAGE_SHIFT, 0); + if ((PLAT_NODE_DATA_STARTNR(node) + + PLAT_NODE_DATA_SIZE(node)) > pagenr) + pagenr = PLAT_NODE_DATA_STARTNR(node) + + PLAT_NODE_DATA_SIZE(node); + } +} + +void __init mem_init(void) +{ + extern char _stext, _etext, _fdata, _edata; + extern char __init_begin, __init_end; + extern unsigned long totalram_pages; + extern unsigned long setup_zero_pages(void); + cnodeid_t nid; + unsigned long tmp, ram; + unsigned long codesize, reservedpages, datasize, initsize; + int slot, numslots; + struct page *pg, *pslot; + pfn_t pgnr; + + num_physpages = numpages; /* memory already sized by szmem */ + max_mapnr = pagenr; /* already found during paging_init */ + high_memory = (void *) __va(max_mapnr << PAGE_SHIFT); + + for (nid = 0; nid < numnodes; nid++) { + + /* + * Hack till free_area_init_core() zeroes free_pages + */ + for (tmp = 0; tmp < MAX_NR_ZONES; tmp++) + PLAT_NODE_DATA(nid)->gendata.node_zones[tmp].free_pages=0; + /* + * This will free up the bootmem, ie, slot 0 memory. + */ + totalram_pages += free_all_bootmem_node(nid); + + /* + * We need to manually do the other slots. + */ + pg = NODE_DATA(nid)->node_mem_map + slot_getsize(nid, 0); + pgnr = PLAT_NODE_DATA_STARTNR(nid) + slot_getsize(nid, 0); + numslots = node_getlastslot(nid); + for (slot = 1; slot <= numslots; slot++) { + pslot = NODE_DATA(nid)->node_mem_map + + slot_getbasepfn(nid, slot) - slot_getbasepfn(nid, 0); + + /* + * Mark holes in previous slot. May also want to + * free up the pages that hold the memmap entries. + */ + while (pg < pslot) { + pg->flags |= (1<<PG_skip); + pg++; pgnr++; + } + + /* + * Free valid memory in current slot. + */ + pslot += slot_getsize(nid, slot); + while (pg < pslot) { + if (!page_is_ram(pgnr)) + continue; + ClearPageReserved(pg); + atomic_set(&pg->count, 1); + __free_page(pg); + totalram_pages++; + pg++; pgnr++; + } + } + } + + totalram_pages -= setup_zero_pages(); /* This comes from node 0 */ + + reservedpages = ram = 0; + for (nid = 0; nid < numnodes; nid++) { + for (tmp = PLAT_NODE_DATA_STARTNR(nid); tmp < + (PLAT_NODE_DATA_STARTNR(nid) + + PLAT_NODE_DATA_SIZE(nid)); tmp++) { + /* Ignore holes */ + if (PageSkip(mem_map+tmp)) + continue; + if (page_is_ram(tmp)) { + ram++; + if (PageReserved(mem_map+tmp)) + reservedpages++; + } + } + } + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_fdata; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " + "%ldk data, %ldk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + ram << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); +} + +#endif /* CONFIG_DISCONTIGMEM */ |