init.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,842 行 · 第 1/4 页

C
1,842
字号
/*  $Id: init.c,v 1.209 2002/02/09 19:49:31 davem Exp $ *  arch/sparc64/mm/init.c * *  Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) *  Copyright (C) 1997-1999 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ #include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/init.h>#include <linux/bootmem.h>#include <linux/mm.h>#include <linux/hugetlb.h>#include <linux/slab.h>#include <linux/initrd.h>#include <linux/swap.h>#include <linux/pagemap.h>#include <linux/fs.h>#include <linux/seq_file.h>#include <linux/kprobes.h>#include <linux/cache.h>#include <linux/sort.h>#include <asm/head.h>#include <asm/system.h>#include <asm/page.h>#include <asm/pgalloc.h>#include <asm/pgtable.h>#include <asm/oplib.h>#include <asm/iommu.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/mmu_context.h>#include <asm/tlbflush.h>#include <asm/dma.h>#include <asm/starfire.h>#include <asm/tlb.h>#include <asm/spitfire.h>#include <asm/sections.h>#include <asm/tsb.h>#include <asm/hypervisor.h>extern void device_scan(void);#define MAX_PHYS_ADDRESS	(1UL << 42UL)#define KPTE_BITMAP_CHUNK_SZ	(256UL * 1024UL * 1024UL)#define KPTE_BITMAP_BYTES	\	((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8)unsigned long kern_linear_pte_xor[2] __read_mostly;/* A bitmap, one bit for every 256MB of physical memory.  If the bit * is clear, we should use a 4MB page (via kern_linear_pte_xor[0]) else * if set we should use a 256MB page (via kern_linear_pte_xor[1]). */unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];/* A special kernel TSB for 4MB and 256MB linear mappings.  */struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];#define MAX_BANKS	32static struct linux_prom64_registers pavail[MAX_BANKS] __initdata;static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;static int pavail_ents __initdata;static int pavail_rescan_ents __initdata;static int cmp_p64(const void *a, const void *b){	const struct linux_prom64_registers *x = a, *y = b;	if (x->phys_addr > y->phys_addr)		return 1;	if (x->phys_addr < y->phys_addr)		return -1;	return 0;}static void __init read_obp_memory(const char *property,				   struct linux_prom64_registers *regs,				   int *num_ents){	int node = prom_finddevice("/memory");	int prop_size = prom_getproplen(node, property);	int ents, ret, i;	ents = prop_size / sizeof(struct linux_prom64_registers);	if (ents > MAX_BANKS) {		prom_printf("The machine has more %s property entries than "			    "this kernel can support (%d).\n",			    property, MAX_BANKS);		prom_halt();	}	ret = prom_getproperty(node, property, (char *) regs, prop_size);	if (ret == -1) {		prom_printf("Couldn't get %s property from /memory.\n");		prom_halt();	}	*num_ents = ents;	/* Sanitize what we got from the firmware, by page aligning	 * everything.	 */	for (i = 0; i < ents; i++) {		unsigned long base, size;		base = regs[i].phys_addr;		size = regs[i].reg_size;		size &= PAGE_MASK;		if (base & ~PAGE_MASK) {			unsigned long new_base = PAGE_ALIGN(base);			size -= new_base - base;			if ((long) size < 0L)				size = 0UL;			base = new_base;		}		regs[i].phys_addr = base;		regs[i].reg_size = size;	}	sort(regs, ents, sizeof(struct linux_prom64_registers),	     cmp_p64, NULL);}unsigned long *sparc64_valid_addr_bitmap __read_mostly;/* Kernel physical address base and size in bytes.  */unsigned long kern_base __read_mostly;unsigned long kern_size __read_mostly;/* get_new_mmu_context() uses "cache + 1".  */DEFINE_SPINLOCK(ctx_alloc_lock);unsigned long tlb_context_cache = CTX_FIRST_VERSION - 1;#define CTX_BMAP_SLOTS (1UL << (CTX_NR_BITS - 6))unsigned long mmu_context_bmap[CTX_BMAP_SLOTS];/* References to special section boundaries */extern char  _start[], _end[];/* Initial ramdisk setup */extern unsigned long sparc_ramdisk_image64;extern unsigned int sparc_ramdisk_image;extern unsigned int sparc_ramdisk_size;struct page *mem_map_zero __read_mostly;unsigned int sparc64_highest_unlocked_tlb_ent __read_mostly;unsigned long sparc64_kern_pri_context __read_mostly;unsigned long sparc64_kern_pri_nuc_bits __read_mostly;unsigned long sparc64_kern_sec_context __read_mostly;int bigkernel = 0;kmem_cache_t *pgtable_cache __read_mostly;static void zero_ctor(void *addr, kmem_cache_t *cache, unsigned long flags){	clear_page(addr);}extern void tsb_cache_init(void);void pgtable_cache_init(void){	pgtable_cache = kmem_cache_create("pgtable_cache",					  PAGE_SIZE, PAGE_SIZE,					  SLAB_HWCACHE_ALIGN |					  SLAB_MUST_HWCACHE_ALIGN,					  zero_ctor,					  NULL);	if (!pgtable_cache) {		prom_printf("Could not create pgtable_cache\n");		prom_halt();	}	tsb_cache_init();}#ifdef CONFIG_DEBUG_DCFLUSHatomic_t dcpage_flushes = ATOMIC_INIT(0);#ifdef CONFIG_SMPatomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);#endif#endifinline void flush_dcache_page_impl(struct page *page){	BUG_ON(tlb_type == hypervisor);#ifdef CONFIG_DEBUG_DCFLUSH	atomic_inc(&dcpage_flushes);#endif#ifdef DCACHE_ALIASING_POSSIBLE	__flush_dcache_page(page_address(page),			    ((tlb_type == spitfire) &&			     page_mapping(page) != NULL));#else	if (page_mapping(page) != NULL &&	    tlb_type == spitfire)		__flush_icache_page(__pa(page_address(page)));#endif}#define PG_dcache_dirty		PG_arch_1#define PG_dcache_cpu_shift	24UL#define PG_dcache_cpu_mask	(256UL - 1UL)#if NR_CPUS > 256#error D-cache dirty tracking and thread_info->cpu need fixing for > 256 cpus#endif#define dcache_dirty_cpu(page) \	(((page)->flags >> PG_dcache_cpu_shift) & PG_dcache_cpu_mask)static __inline__ void set_dcache_dirty(struct page *page, int this_cpu){	unsigned long mask = this_cpu;	unsigned long non_cpu_bits;	non_cpu_bits = ~(PG_dcache_cpu_mask << PG_dcache_cpu_shift);	mask = (mask << PG_dcache_cpu_shift) | (1UL << PG_dcache_dirty);	__asm__ __volatile__("1:\n\t"			     "ldx	[%2], %%g7\n\t"			     "and	%%g7, %1, %%g1\n\t"			     "or	%%g1, %0, %%g1\n\t"			     "casx	[%2], %%g7, %%g1\n\t"			     "cmp	%%g7, %%g1\n\t"			     "membar	#StoreLoad | #StoreStore\n\t"			     "bne,pn	%%xcc, 1b\n\t"			     " nop"			     : /* no outputs */			     : "r" (mask), "r" (non_cpu_bits), "r" (&page->flags)			     : "g1", "g7");}static __inline__ void clear_dcache_dirty_cpu(struct page *page, unsigned long cpu){	unsigned long mask = (1UL << PG_dcache_dirty);	__asm__ __volatile__("! test_and_clear_dcache_dirty\n"			     "1:\n\t"			     "ldx	[%2], %%g7\n\t"			     "srlx	%%g7, %4, %%g1\n\t"			     "and	%%g1, %3, %%g1\n\t"			     "cmp	%%g1, %0\n\t"			     "bne,pn	%%icc, 2f\n\t"			     " andn	%%g7, %1, %%g1\n\t"			     "casx	[%2], %%g7, %%g1\n\t"			     "cmp	%%g7, %%g1\n\t"			     "membar	#StoreLoad | #StoreStore\n\t"			     "bne,pn	%%xcc, 1b\n\t"			     " nop\n"			     "2:"			     : /* no outputs */			     : "r" (cpu), "r" (mask), "r" (&page->flags),			       "i" (PG_dcache_cpu_mask),			       "i" (PG_dcache_cpu_shift)			     : "g1", "g7");}static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long pte){	unsigned long tsb_addr = (unsigned long) ent;	if (tlb_type == cheetah_plus || tlb_type == hypervisor)		tsb_addr = __pa(tsb_addr);	__tsb_insert(tsb_addr, tag, pte);}unsigned long _PAGE_ALL_SZ_BITS __read_mostly;unsigned long _PAGE_SZBITS __read_mostly;void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte){	struct mm_struct *mm;	struct tsb *tsb;	unsigned long tag, flags;	unsigned long tsb_index, tsb_hash_shift;	if (tlb_type != hypervisor) {		unsigned long pfn = pte_pfn(pte);		unsigned long pg_flags;		struct page *page;		if (pfn_valid(pfn) &&		    (page = pfn_to_page(pfn), page_mapping(page)) &&		    ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {			int cpu = ((pg_flags >> PG_dcache_cpu_shift) &				   PG_dcache_cpu_mask);			int this_cpu = get_cpu();			/* This is just to optimize away some function calls			 * in the SMP case.			 */			if (cpu == this_cpu)				flush_dcache_page_impl(page);			else				smp_flush_dcache_page_impl(page, cpu);			clear_dcache_dirty_cpu(page, cpu);			put_cpu();		}	}	mm = vma->vm_mm;	tsb_index = MM_TSB_BASE;	tsb_hash_shift = PAGE_SHIFT;	spin_lock_irqsave(&mm->context.lock, flags);#ifdef CONFIG_HUGETLB_PAGE	if (mm->context.tsb_block[MM_TSB_HUGE].tsb != NULL) {		if ((tlb_type == hypervisor &&		     (pte_val(pte) & _PAGE_SZALL_4V) == _PAGE_SZHUGE_4V) ||		    (tlb_type != hypervisor &&		     (pte_val(pte) & _PAGE_SZALL_4U) == _PAGE_SZHUGE_4U)) {			tsb_index = MM_TSB_HUGE;			tsb_hash_shift = HPAGE_SHIFT;		}	}#endif	tsb = mm->context.tsb_block[tsb_index].tsb;	tsb += ((address >> tsb_hash_shift) &		(mm->context.tsb_block[tsb_index].tsb_nentries - 1UL));	tag = (address >> 22UL);	tsb_insert(tsb, tag, pte_val(pte));	spin_unlock_irqrestore(&mm->context.lock, flags);}void flush_dcache_page(struct page *page){	struct address_space *mapping;	int this_cpu;	if (tlb_type == hypervisor)		return;	/* Do not bother with the expensive D-cache flush if it	 * is merely the zero page.  The 'bigcore' testcase in GDB	 * causes this case to run millions of times.	 */	if (page == ZERO_PAGE(0))		return;	this_cpu = get_cpu();	mapping = page_mapping(page);	if (mapping && !mapping_mapped(mapping)) {		int dirty = test_bit(PG_dcache_dirty, &page->flags);		if (dirty) {			int dirty_cpu = dcache_dirty_cpu(page);			if (dirty_cpu == this_cpu)				goto out;			smp_flush_dcache_page_impl(page, dirty_cpu);		}		set_dcache_dirty(page, this_cpu);	} else {		/* We could delay the flush for the !page_mapping		 * case too.  But that case is for exec env/arg		 * pages and those are %99 certainly going to get		 * faulted into the tlb (and thus flushed) anyways.		 */		flush_dcache_page_impl(page);	}out:	put_cpu();}void __kprobes flush_icache_range(unsigned long start, unsigned long end){	/* Cheetah and Hypervisor platform cpus have coherent I-cache. */	if (tlb_type == spitfire) {		unsigned long kaddr;		for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE)			__flush_icache_page(__get_phys(kaddr));	}}void show_mem(void){	printk("Mem-info:\n");	show_free_areas();	printk("Free swap:       %6ldkB\n",	       nr_swap_pages << (PAGE_SHIFT-10));	printk("%ld pages of RAM\n", num_physpages);	printk("%d free pages\n", nr_free_pages());}void mmu_info(struct seq_file *m){	if (tlb_type == cheetah)		seq_printf(m, "MMU Type\t: Cheetah\n");	else if (tlb_type == cheetah_plus)		seq_printf(m, "MMU Type\t: Cheetah+\n");	else if (tlb_type == spitfire)		seq_printf(m, "MMU Type\t: Spitfire\n");	else if (tlb_type == hypervisor)		seq_printf(m, "MMU Type\t: Hypervisor (sun4v)\n");	else		seq_printf(m, "MMU Type\t: ???\n");#ifdef CONFIG_DEBUG_DCFLUSH	seq_printf(m, "DCPageFlushes\t: %d\n",		   atomic_read(&dcpage_flushes));#ifdef CONFIG_SMP	seq_printf(m, "DCPageFlushesXC\t: %d\n",		   atomic_read(&dcpage_flushes_xcall));#endif /* CONFIG_SMP */#endif /* CONFIG_DEBUG_DCFLUSH */}struct linux_prom_translation {	unsigned long virt;	unsigned long size;	unsigned long data;};/* Exported for kernel TLB miss handling in ktlb.S */struct linux_prom_translation prom_trans[512] __read_mostly;unsigned int prom_trans_ents __read_mostly;/* Exported for SMP bootup purposes. */unsigned long kern_locked_tte_data;/* The obp translations are saved based on 8k pagesize, since obp can * use a mixture of pagesizes. Misses to the LOW_OBP_ADDRESS -> * HI_OBP_ADDRESS range are handled in ktlb.S. */static inline int in_obp_range(unsigned long vaddr){	return (vaddr >= LOW_OBP_ADDRESS &&		vaddr < HI_OBP_ADDRESS);}static int cmp_ptrans(const void *a, const void *b){	const struct linux_prom_translation *x = a, *y = b;	if (x->virt > y->virt)		return 1;	if (x->virt < y->virt)		return -1;	return 0;}/* Read OBP translations property into 'prom_trans[]'.  */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?