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 + -
显示快捷键?