init.c

来自「linux 内核源代码」· C语言 代码 · 共 1,976 行 · 第 1/4 页

C
1,976
字号
/*  $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/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/poison.h>#include <linux/fs.h>#include <linux/seq_file.h>#include <linux/kprobes.h>#include <linux/cache.h>#include <linux/sort.h>#include <linux/percpu.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>#include <asm/prom.h>#include <asm/sstate.h>#include <asm/mdesc.h>#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)];#ifndef CONFIG_DEBUG_PAGEALLOC/* A special kernel TSB for 4MB and 256MB linear mappings. * Space is allocated for this right after the trap table * in arch/sparc64/kernel/head.S */extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];#endif#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();	}	/* 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;		}		if (size == 0UL) {			/* If it is empty, simply get rid of it.			 * This simplifies the logic of the other			 * functions that process these arrays.			 */			memmove(&regs[i], &regs[i + 1],				(ents - i - 1) * sizeof(regs[0]));			i--;			ents--;			continue;		}		regs[i].phys_addr = base;		regs[i].reg_size = size;	}	*num_ents = ents;	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;/* 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;#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	32UL#define PG_dcache_cpu_mask	\	((1UL<<ilog2(roundup_pow_of_two(NR_CPUS)))-1UL)#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;		/* This code only runs on Spitfire cpus so this is		 * why we can assume _PAGE_PADDR_4U.		 */		for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE) {			unsigned long paddr, mask = _PAGE_PADDR_4U;			if (kaddr >= PAGE_OFFSET)				paddr = kaddr & mask;			else {				pgd_t *pgdp = pgd_offset_k(kaddr);				pud_t *pudp = pud_offset(pgdp, kaddr);				pmd_t *pmdp = pmd_offset(pudp, kaddr);				pte_t *ptep = pte_offset_kernel(pmdp, kaddr);				paddr = pte_val(*ptep) & mask;			}			__flush_icache_page(paddr);		}	}}void show_mem(void){	unsigned long total = 0, reserved = 0;	unsigned long shared = 0, cached = 0;	pg_data_t *pgdat;	printk(KERN_INFO "Mem-info:\n");	show_free_areas();	printk(KERN_INFO "Free swap:       %6ldkB\n",	       nr_swap_pages << (PAGE_SHIFT-10));	for_each_online_pgdat(pgdat) {		unsigned long i, flags;		pgdat_resize_lock(pgdat, &flags);		for (i = 0; i < pgdat->node_spanned_pages; i++) {			struct page *page = pgdat_page_nr(pgdat, i);			total++;			if (PageReserved(page))				reserved++;			else if (PageSwapCache(page))				cached++;			else if (page_count(page))				shared += page_count(page) - 1;		}		pgdat_resize_unlock(pgdat, &flags);	}	printk(KERN_INFO "%lu pages of RAM\n", total);	printk(KERN_INFO "%lu reserved pages\n", reserved);	printk(KERN_INFO "%lu pages shared\n", shared);	printk(KERN_INFO "%lu pages swap cached\n", cached);	printk(KERN_INFO "%lu pages dirty\n",	       global_page_state(NR_FILE_DIRTY));	printk(KERN_INFO "%lu pages writeback\n",	       global_page_state(NR_WRITEBACK));	printk(KERN_INFO "%lu pages mapped\n",	       global_page_state(NR_FILE_MAPPED));	printk(KERN_INFO "%lu pages slab\n",		global_page_state(NR_SLAB_RECLAIMABLE) +		global_page_state(NR_SLAB_UNRECLAIMABLE));	printk(KERN_INFO "%lu pages pagetables\n",	       global_page_state(NR_PAGETABLE));}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;}

⌨️ 快捷键说明

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