⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 init.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/*  $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/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>extern void device_scan(void);#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;/* Ugly, but necessary... -DaveM */unsigned long phys_base __read_mostly;unsigned long kern_base __read_mostly;unsigned long kern_size __read_mostly;unsigned long pfn_base __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;/* XXX Tune this... */#define PGT_CACHE_LOW	25#define PGT_CACHE_HIGH	50void check_pgt_cache(void){	preempt_disable();	if (pgtable_cache_size > PGT_CACHE_HIGH) {		do {			if (pgd_quicklist)				free_pgd_slow(get_pgd_fast());			if (pte_quicklist[0])				free_pte_slow(pte_alloc_one_fast(NULL, 0));			if (pte_quicklist[1])				free_pte_slow(pte_alloc_one_fast(NULL, 1 << (PAGE_SHIFT + 10)));		} while (pgtable_cache_size > PGT_CACHE_LOW);	}	preempt_enable();}#ifdef CONFIG_DEBUG_DCFLUSHatomic_t dcpage_flushes = ATOMIC_INIT(0);#ifdef CONFIG_SMPatomic_t dcpage_flushes_xcall = ATOMIC_INIT(0);#endif#endif__inline__ void flush_dcache_page_impl(struct page *page){#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	24#define PG_dcache_cpu_mask	(256 - 1)#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");}void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte){	struct page *page;	unsigned long pfn;	unsigned long pg_flags;	pfn = pte_pfn(pte);	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();	}}void flush_dcache_page(struct page *page){	struct address_space *mapping;	int this_cpu;	/* 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 has coherent I-cache. */	if (tlb_type == spitfire) {		unsigned long kaddr;		for (kaddr = start; kaddr < end; kaddr += PAGE_SIZE)			__flush_icache_page(__get_phys(kaddr));	}}unsigned long page_to_pfn(struct page *page){	return (unsigned long) ((page - mem_map) + pfn_base);}struct page *pfn_to_page(unsigned long pfn){	return (mem_map + (pfn - pfn_base));}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());	printk("%d pages in page table cache\n",pgtable_cache_size);}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		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;unsigned int swapper_pgd_zero __read_mostly;extern unsigned long prom_boot_page;extern void prom_remap(unsigned long physpage, unsigned long virtpage, int mmu_ihandle);extern int prom_get_mmu_ihandle(void);extern void register_prom_callbacks(void);/* Exported for SMP bootup purposes. */unsigned long kern_locked_tte_data;/* * Translate PROM's mapping we capture at boot time into physical address. * The second parameter is only set from prom_callback() invocations. */unsigned long prom_virt_to_phys(unsigned long promva, int *error){	int i;	for (i = 0; i < prom_trans_ents; i++) {		struct linux_prom_translation *p = &prom_trans[i];		if (promva >= p->virt &&		    promva < (p->virt + p->size)) {			unsigned long base = p->data & _PAGE_PADDR;			if (error)				*error = 0;			return base + (promva & (8192 - 1));		}	}	if (error)		*error = 1;	return 0UL;}/* 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 and do not use the vpte * scheme (also, see rant in inherit_locked_prom_mappings()). */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[]'.  */static void __init read_obp_translations(void){	int n, node, ents, first, last, i;	node = prom_finddevice("/virtual-memory");	n = prom_getproplen(node, "translations");	if (unlikely(n == 0 || n == -1)) {		prom_printf("prom_mappings: Couldn't get size.\n");		prom_halt();	}	if (unlikely(n > sizeof(prom_trans))) {		prom_printf("prom_mappings: Size %Zd is too big.\n", n);		prom_halt();	}	if ((n = prom_getproperty(node, "translations",				  (char *)&prom_trans[0],				  sizeof(prom_trans))) == -1) {		prom_printf("prom_mappings: Couldn't get property.\n");		prom_halt();	}	n = n / sizeof(struct linux_prom_translation);	ents = n;	sort(prom_trans, ents, sizeof(struct linux_prom_translation),	     cmp_ptrans, NULL);	/* Now kick out all the non-OBP entries.  */	for (i = 0; i < ents; i++) {		if (in_obp_range(prom_trans[i].virt))			break;	}	first = i;	for (; i < ents; i++) {		if (!in_obp_range(prom_trans[i].virt))			break;	}	last = i;	for (i = 0; i < (last - first); i++) {		struct linux_prom_translation *src = &prom_trans[i + first];		struct linux_prom_translation *dest = &prom_trans[i];		*dest = *src;	}	for (; i < ents; i++) {		struct linux_prom_translation *dest = &prom_trans[i];		dest->virt = dest->size = dest->data = 0x0UL;	}	prom_trans_ents = last - first;	if (tlb_type == spitfire) {		/* Clear diag TTE bits. */		for (i = 0; i < prom_trans_ents; i++)			prom_trans[i].data &= ~0x0003fe0000000000UL;	}}static void __init remap_kernel(void){	unsigned long phys_page, tte_vaddr, tte_data;	int tlb_ent = sparc64_highest_locked_tlbent();	tte_vaddr = (unsigned long) KERNBASE;	phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL;	tte_data = (phys_page | (_PAGE_VALID | _PAGE_SZ4MB |				 _PAGE_CP | _PAGE_CV | _PAGE_P |				 _PAGE_L | _PAGE_W));	kern_locked_tte_data = tte_data;	/* Now lock us into the TLBs via OBP. */	prom_dtlb_load(tlb_ent, tte_data, tte_vaddr);	prom_itlb_load(tlb_ent, tte_data, tte_vaddr);	if (bigkernel) {		tlb_ent -= 1;		prom_dtlb_load(tlb_ent,			       tte_data + 0x400000, 			       tte_vaddr + 0x400000);		prom_itlb_load(tlb_ent,			       tte_data + 0x400000, 			       tte_vaddr + 0x400000);	}	sparc64_highest_unlocked_tlb_ent = tlb_ent - 1;	if (tlb_type == cheetah_plus) {		sparc64_kern_pri_context = (CTX_CHEETAH_PLUS_CTX0 |					    CTX_CHEETAH_PLUS_NUC);		sparc64_kern_pri_nuc_bits = CTX_CHEETAH_PLUS_NUC;		sparc64_kern_sec_context = CTX_CHEETAH_PLUS_CTX0;	}}static void __init inherit_prom_mappings(void){	read_obp_translations();	/* Now fixup OBP's idea about where we really are mapped. */	prom_printf("Remapping the kernel... ");	remap_kernel();	prom_printf("done.\n");	prom_printf("Registering callbacks... ");	register_prom_callbacks();	prom_printf("done.\n");}/* The OBP specifications for sun4u mark 0xfffffffc00000000 and * upwards as reserved for use by the firmware (I wonder if this * will be the same on Cheetah...).  We use this virtual address * range for the VPTE table mappings of the nucleus so we need * to zap them when we enter the PROM.  -DaveM */static void __flush_nucleus_vptes(void){	unsigned long prom_reserved_base = 0xfffffffc00000000UL;	int i;	/* Only DTLB must be checked for VPTE entries. */	if (tlb_type == spitfire) {		for (i = 0; i < 63; i++) {			unsigned long tag;			/* Spitfire Errata #32 workaround */			/* NOTE: Always runs on spitfire, so no cheetah+			 *       page size encodings.

⌨️ 快捷键说明

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