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(®s[i], ®s[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 + -
显示快捷键?