📄 init.c
字号:
/* $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 + -