📄 init.c
字号:
/* * $Id: init.c,v 1.195 1999/10/15 16:39:39 cort Exp $ * * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */#include <linux/config.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/types.h>#include <linux/ptrace.h>#include <linux/mman.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/stddef.h>#include <linux/vmalloc.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/openpic.h>#include <linux/bootmem.h>#include <linux/highmem.h>#ifdef CONFIG_BLK_DEV_INITRD#include <linux/blk.h> /* for initrd_* */#endif#include <asm/pgalloc.h>#include <asm/prom.h>#include <asm/io.h>#include <asm/mmu_context.h>#include <asm/pgtable.h>#include <asm/mmu.h>#include <asm/residual.h>#include <asm/uaccess.h>#ifdef CONFIG_8xx#include <asm/8xx_immap.h>#include <asm/mpc8xx.h>#endif#ifdef CONFIG_8260#include <asm/immap_8260.h>#include <asm/mpc8260.h>#endif#include <asm/smp.h>#include <asm/bootx.h>#include <asm/machdep.h>#include <asm/setup.h>#include <asm/amigahw.h>#include <asm/gemini.h>#include "mem_pieces.h"#if defined(CONFIG_4xx)#include "4xx_tlb.h"#endif#define MAX_LOW_MEM (640 << 20)#define PGTOKB(pages) (((pages) * PAGE_SIZE) >> 10)int prom_trashed;atomic_t next_mmu_context;unsigned long *end_of_DRAM;unsigned long total_memory;unsigned long total_lowmem;int mem_init_done;int init_bootmem_done;int boot_mapsize;unsigned long totalram_pages;unsigned long totalhigh_pages;extern pgd_t swapper_pg_dir[];extern char _start[], _end[];extern char etext[], _stext[];extern char __init_begin, __init_end;extern char __prep_begin, __prep_end;extern char __chrp_begin, __chrp_end;extern char __pmac_begin, __pmac_end;extern char __apus_begin, __apus_end;extern char __openfirmware_begin, __openfirmware_end;struct device_node *memory_node;unsigned long ioremap_base;unsigned long ioremap_bot;unsigned long avail_start;extern int num_memory;extern struct mem_info memory[];extern boot_infos_t *boot_infos;extern unsigned int rtas_data, rtas_size;#ifndef CONFIG_SMPstruct pgtable_cache_struct quicklists;#endif#ifdef CONFIG_HIGHMEMpte_t *kmap_pte;pgprot_t kmap_prot;#endifvoid MMU_init(void);static void *MMU_get_page(void);unsigned long prep_find_end_of_memory(void);unsigned long pmac_find_end_of_memory(void);unsigned long apus_find_end_of_memory(void);unsigned long gemini_find_end_of_memory(void);extern unsigned long find_end_of_memory(void);#ifdef CONFIG_8xxunsigned long m8xx_find_end_of_memory(void);#endif /* CONFIG_8xx */#ifdef CONFIG_4xxunsigned long oak_find_end_of_memory(void);#endif#ifdef CONFIG_8260unsigned long m8260_find_end_of_memory(void);#endif /* CONFIG_8260 */static void mapin_ram(void);void map_page(unsigned long va, unsigned long pa, int flags);void set_phys_avail(struct mem_pieces *mp);extern void die_if_kernel(char *,struct pt_regs *,long);extern char _start[], _end[];extern char _stext[], etext[];extern struct task_struct *current_set[NR_CPUS];struct mem_pieces phys_mem;char *klimit = _end;struct mem_pieces phys_avail;PTE *Hash, *Hash_end;unsigned long Hash_size, Hash_mask;#if !defined(CONFIG_4xx) && !defined(CONFIG_8xx)unsigned long _SDR1;static void hash_init(void);union ubat { /* BAT register values to be loaded */ BAT bat;#ifdef CONFIG_PPC64BRIDGE u64 word[2];#else u32 word[2];#endif } BATS[4][2]; /* 4 pairs of IBAT, DBAT */struct batrange { /* stores address ranges mapped by BATs */ unsigned long start; unsigned long limit; unsigned long phys;} bat_addrs[4];/* * Return PA for this VA if it is mapped by a BAT, or 0 */static inline unsigned long v_mapped_by_bats(unsigned long va){ int b; for (b = 0; b < 4; ++b) if (va >= bat_addrs[b].start && va < bat_addrs[b].limit) return bat_addrs[b].phys + (va - bat_addrs[b].start); return 0;}/* * Return VA for a given PA or 0 if not mapped */static inline unsigned long p_mapped_by_bats(unsigned long pa){ int b; for (b = 0; b < 4; ++b) if (pa >= bat_addrs[b].phys && pa < (bat_addrs[b].limit-bat_addrs[b].start) +bat_addrs[b].phys) return bat_addrs[b].start+(pa-bat_addrs[b].phys); return 0;}#else /* CONFIG_4xx || CONFIG_8xx */#define v_mapped_by_bats(x) (0UL)#define p_mapped_by_bats(x) (0UL)#endif /* !CONFIG_4xx && !CONFIG_8xx *//* * this tells the system to map all of ram with the segregs * (i.e. page tables) instead of the bats. * -- Cort */int __map_without_bats;/* max amount of RAM to use */unsigned long __max_memory;void __bad_pte(pmd_t *pmd){ printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); pmd_val(*pmd) = (unsigned long) BAD_PAGETABLE;}pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset){ pte_t *pte; if (pmd_none(*pmd)) { if (!mem_init_done) pte = (pte_t *) MMU_get_page(); else if ((pte = (pte_t *) __get_free_page(GFP_KERNEL))) clear_page(pte); if (pte) { pmd_val(*pmd) = (unsigned long)pte; return pte + offset; } pmd_val(*pmd) = (unsigned long)BAD_PAGETABLE; return NULL; } if (pmd_bad(*pmd)) { __bad_pte(pmd); return NULL; } return (pte_t *) pmd_page(*pmd) + offset;}int do_check_pgt_cache(int low, int high){ int freed = 0; if(pgtable_cache_size > high) { do { if(pgd_quicklist) free_pgd_slow(get_pgd_fast()), freed++; if(pmd_quicklist) free_pmd_slow(get_pmd_fast()), freed++; if(pte_quicklist) free_pte_slow(get_pte_fast()), freed++; } while(pgtable_cache_size > low); } return freed;}/* * BAD_PAGE is the page that is used for page faults when linux * is out-of-memory. Older versions of linux just did a * do_exit(), but using this instead means there is less risk * for a process dying in kernel mode, possibly leaving a inode * unused etc.. * * BAD_PAGETABLE is the accompanying page-table: it is initialized * to point to BAD_PAGE entries. * * ZERO_PAGE is a special page that is used for zero-initialized * data and COW. */pte_t *empty_bad_page_table;pte_t * __bad_pagetable(void){ clear_page(empty_bad_page_table); return empty_bad_page_table;}void *empty_bad_page;pte_t __bad_page(void){ clear_page(empty_bad_page); return pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED));}void show_mem(void){ int i,free = 0,total = 0,reserved = 0; int shared = 0, cached = 0; struct task_struct *p; int highmem = 0; printk("Mem-info:\n"); show_free_areas(); printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); i = max_mapnr; while (i-- > 0) { total++; if (PageHighMem(mem_map+i)) highmem++; if (PageReserved(mem_map+i)) reserved++; else if (PageSwapCache(mem_map+i)) cached++; else if (!page_count(mem_map+i)) free++; else shared += atomic_read(&mem_map[i].count) - 1; } printk("%d pages of RAM\n",total); printk("%d pages of HIGHMEM\n", highmem); printk("%d free pages\n",free); printk("%d reserved pages\n",reserved); printk("%d pages shared\n",shared); printk("%d pages swap cached\n",cached); printk("%d pages in page table cache\n",(int)pgtable_cache_size); show_buffers(); printk("%-8s %3s %8s %8s %8s %9s %8s", "Process", "Pid", "Ctx", "Ctx<<4", "Last Sys", "pc", "task");#ifdef CONFIG_SMP printk(" %3s", "CPU");#endif /* CONFIG_SMP */ printk("\n"); for_each_task(p) { printk("%-8.8s %3d %8ld %8ld %8ld %c%08lx %08lx ", p->comm,p->pid, (p->mm)?p->mm->context:0, (p->mm)?(p->mm->context<<4):0, p->thread.last_syscall, (p->thread.regs)?user_mode(p->thread.regs) ? 'u' : 'k' : '?', (p->thread.regs)?p->thread.regs->nip:0, (ulong)p); { int iscur = 0;#ifdef CONFIG_SMP printk("%3d ", p->processor); if ( (p->processor != NO_PROC_ID) && (p == current_set[p->processor]) ) { iscur = 1; printk("current"); }#else if ( p == current ) { iscur = 1; printk("current"); } if ( p == last_task_used_math ) { if ( iscur ) printk(","); printk("last math"); } #endif /* CONFIG_SMP */ printk("\n"); } }}void si_meminfo(struct sysinfo *val){ int i; i = max_mapnr; val->totalram = 0; val->sharedram = 0; val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); while (i-- > 0) { if (PageReserved(mem_map+i)) continue; val->totalram++; if (!atomic_read(&mem_map[i].count)) continue; val->sharedram += atomic_read(&mem_map[i].count) - 1; } val->totalhigh = totalhigh_pages; val->freehigh = nr_free_highpages(); val->mem_unit = PAGE_SIZE;}void *ioremap(unsigned long addr, unsigned long size){ return __ioremap(addr, size, _PAGE_NO_CACHE);}void *__ioremap(unsigned long addr, unsigned long size, unsigned long flags){ unsigned long p, v, i; /* * Choose an address to map it to. * Once the vmalloc system is running, we use it. * Before then, we map addresses >= ioremap_base * virt == phys; for addresses below this we use * space going down from ioremap_base (ioremap_bot * records where we're up to). */ p = addr & PAGE_MASK; size = PAGE_ALIGN(addr + size) - p; /* * If the address lies within the first 16 MB, assume it's in ISA * memory space */ if (p < 16*1024*1024) p += _ISA_MEM_BASE; /* * Don't allow anybody to remap normal RAM that we're using. * mem_init() sets high_memory so only do the check after that. */ if ( mem_init_done && (p < virt_to_phys(high_memory)) ) { printk("__ioremap(): phys addr %0lx is RAM lr %p\n", p, __builtin_return_address(0)); return NULL; } if (size == 0) return NULL; /* * Is it already mapped? Perhaps overlapped by a previous * BAT mapping. If the whole area is mapped then we're done, * otherwise remap it since we want to keep the virt addrs for * each request contiguous. * * We make the assumption here that if the bottom and top * of the range we want are mapped then it's mapped to the * same virt address (and this is contiguous). * -- Cort */ if ((v = p_mapped_by_bats(p)) /*&& p_mapped_by_bats(p+size-1)*/ ) goto out; if (mem_init_done) { struct vm_struct *area; area = get_vm_area(size, VM_IOREMAP); if (area == 0) return NULL; v = VMALLOC_VMADDR(area->addr); } else { if (p >= ioremap_base) v = p; else v = (ioremap_bot -= size); } if ((flags & _PAGE_PRESENT) == 0) flags |= pgprot_val(PAGE_KERNEL); if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) flags |= _PAGE_GUARDED; /* * Is it a candidate for a BAT mapping? */ for (i = 0; i < size; i += PAGE_SIZE) map_page(v+i, p+i, flags);out: return (void *) (v + (addr & ~PAGE_MASK));}void iounmap(void *addr){ if (addr > high_memory && (unsigned long) addr < ioremap_bot) vfree((void *) (PAGE_MASK & (unsigned long) addr));}unsigned long iopa(unsigned long addr){ unsigned long pa; pmd_t *pd; pte_t *pg; /* Check the BATs */ pa = v_mapped_by_bats(addr); if (pa) return pa; /* Do we have a page table? */ if (init_mm.pgd == NULL) return 0; /* Use upper 10 bits of addr to index the first level map */ pd = (pmd_t *) (init_mm.pgd + (addr >> PGDIR_SHIFT)); if (pmd_none(*pd)) return 0; /* Use middle 10 bits of addr to index the second-level map */ pg = pte_offset(pd, addr); return (pte_val(*pg) & PAGE_MASK) | (addr & ~PAGE_MASK);}voidmap_page(unsigned long va, unsigned long pa, int flags){ pmd_t *pd, oldpd; pte_t *pg; /* Use upper 10 bits of VA to index the first level map */ pd = pmd_offset(pgd_offset_k(va), va); oldpd = *pd; /* Use middle 10 bits of VA to index the second-level map */ pg = pte_alloc(pd, va); if (pmd_none(oldpd) && mem_init_done) set_pgdir(va, *(pgd_t *)pd); set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); if (mem_init_done) flush_hash_page(0, va);}#ifndef CONFIG_8xx/* * TLB flushing: * * - flush_tlb_all() flushes all processes TLBs * - flush_tlb_mm(mm) flushes the specified mm context TLB's * - flush_tlb_page(vma, vmaddr) flushes one page * - flush_tlb_range(mm, start, end) flushes a range of pages * * since the hardware hash table functions as an extension of the * tlb as far as the linux tables are concerned, flush it too. * -- Cort *//* * Flush all tlb/hash table entries (except perhaps for those * mapping RAM starting at PAGE_OFFSET, since they never change). */voidlocal_flush_tlb_all(void){#ifdef CONFIG_PPC64BRIDGE /* XXX this assumes that the vmalloc arena starts no lower than * 0xd0000000 on 64-bit machines. */ flush_hash_segments(0xd, 0xffffff);#else __clear_user(Hash, Hash_size); _tlbia();#ifdef CONFIG_SMP smp_send_tlb_invalidate(0);#endif /* CONFIG_SMP */#endif /* CONFIG_PPC64BRIDGE */}/* * Flush all the (user) entries for the address space described * by mm. We can't rely on mm->mmap describing all the entries * that might be in the hash table.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -