We find the runtime address of _stext and relocate ourselves based on the following calculation. virtual_base = ALIGN(KERNELBASE,KERNEL_TLB_PIN_SIZE) + MODULO(_stext.run,KERNEL_TLB_PIN_SIZE) relocate() is called with the Effective Virtual Base Address (as shown below) | Phys. Addr| Virt. Addr | Page |------------------------| Boundary | | | | | | | | | Kernel Load |___________|_ __ _ _ _ _|<- Effective Addr(_stext)| | ^ |Virt. Base Addr | | | | | | | | | |reloc_offset| | | | | | | | | | |______v_____|<-(KERNELBASE)%TLB_SIZE | | | | | | | | | Page |-----------|------------| Boundary | | | On BookE, we need __va() & __pa() early in the boot process to access the device tree. Currently this has been defined as : #define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) - PHYSICAL_START + KERNELBASE) where: PHYSICAL_START is kernstart_addr - a variable updated at runtime. KERNELBASE is the compile time Virtual base address of kernel. This won't work for us, as kernstart_addr is dynamic and will yield different results for __va()/__pa() for same mapping. e.g., Let the kernel be loaded at 64MB and KERNELBASE be 0xc0000000 (same as PAGE_OFFSET). In this case, we would be mapping 0 to 0xc0000000, and kernstart_addr = 64M Now __va(1MB) = (0x100000) - (0x4000000) + 0xc0000000 = 0xbc100000 , which is wrong. it should be : 0xc0000000 + 0x100000 = 0xc0100000 On platforms which support AMP, like PPC_47x (based on 44x), the kernel could be loaded at highmem. Hence we cannot always depend on the compile time constants for mapping. Here are the possible solutions: 1) Update kernstart_addr(PHSYICAL_START) to match the Physical address of compile time KERNELBASE value, instead of the actual Physical_Address(_stext). The disadvantage is that we may break other users of PHYSICAL_START. They could be replaced with __pa(_stext). 2) Redefine __va() & __pa() with relocation offset #ifdef CONFIG_RELOCATABLE_PPC32 #define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) - PHYSICAL_START + (KERNELBASE + RELOC_OFFSET))) #define __pa(x) ((unsigned long)(x) + PHYSICAL_START - (KERNELBASE + RELOC_OFFSET)) #endif where, RELOC_OFFSET could be a) A variable, say relocation_offset (like kernstart_addr), updated at boot time. This impacts performance, as we have to load an additional variable from memory. OR b) #define RELOC_OFFSET ((PHYSICAL_START & PPC_PIN_SIZE_OFFSET_MASK) - \ (KERNELBASE & PPC_PIN_SIZE_OFFSET_MASK)) This introduces more calculations for doing the translation. 3) Redefine __va() & __pa() with a new variable i.e, #define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET)) where VIRT_PHYS_OFFSET : #ifdef CONFIG_RELOCATABLE_PPC32 #define VIRT_PHYS_OFFSET virt_phys_offset #else #define VIRT_PHYS_OFFSET (KERNELBASE - PHYSICAL_START) #endif /* CONFIG_RELOCATABLE_PPC32 */ where virt_phy_offset is updated at runtime to : Effective KERNELBASE - kernstart_addr. Taking our example, above: virt_phys_offset = effective_kernelstart_vaddr - kernstart_addr = 0xc0400000 - 0x400000 = 0xc0000000 and __va(0x100000) = 0xc0000000 + 0x100000 = 0xc0100000 which is what we want. I have implemented (3) in the following patch which has same cost of operation as the existing one. I have tested the patches on 440x platforms only. However this should work fine for PPC_47x also, as we only depend on the runtime address and the current TLB XLAT entry for the startup code, which is available in r25. I don't have access to a 47x board yet. So, it would be great if somebody could test this on 47x. Signed-off-by: Suzuki K. Poulose <suzuki@in.ibm.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Kumar Gala <galak@kernel.crashing.org> Cc: linuxppc-dev <linuxppc-dev@lists.ozlabs.org> Signed-off-by: Josh Boyer <jwboyer@gmail.com>
223 lines
5.6 KiB
C
223 lines
5.6 KiB
C
/*
|
|
* 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
|
|
* PPC44x/36-bit changes by Matt Porter (mporter@mvista.com)
|
|
*
|
|
* 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/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/types.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/init.h>
|
|
#include <linux/bootmem.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/initrd.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/hugetlb.h>
|
|
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/prom.h>
|
|
#include <asm/io.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/mmu.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/btext.h>
|
|
#include <asm/tlb.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/system.h>
|
|
#include <asm/hugetlb.h>
|
|
|
|
#include "mmu_decl.h"
|
|
|
|
#if defined(CONFIG_KERNEL_START_BOOL) || defined(CONFIG_LOWMEM_SIZE_BOOL)
|
|
/* The amount of lowmem must be within 0xF0000000 - KERNELBASE. */
|
|
#if (CONFIG_LOWMEM_SIZE > (0xF0000000 - PAGE_OFFSET))
|
|
#error "You must adjust CONFIG_LOWMEM_SIZE or CONFIG_START_KERNEL"
|
|
#endif
|
|
#endif
|
|
#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE
|
|
|
|
phys_addr_t total_memory;
|
|
phys_addr_t total_lowmem;
|
|
|
|
phys_addr_t memstart_addr = (phys_addr_t)~0ull;
|
|
EXPORT_SYMBOL(memstart_addr);
|
|
phys_addr_t kernstart_addr;
|
|
EXPORT_SYMBOL(kernstart_addr);
|
|
|
|
#ifdef CONFIG_RELOCATABLE_PPC32
|
|
/* Used in __va()/__pa() */
|
|
long long virt_phys_offset;
|
|
EXPORT_SYMBOL(virt_phys_offset);
|
|
#endif
|
|
|
|
phys_addr_t lowmem_end_addr;
|
|
|
|
int boot_mapsize;
|
|
#ifdef CONFIG_PPC_PMAC
|
|
unsigned long agp_special_page;
|
|
EXPORT_SYMBOL(agp_special_page);
|
|
#endif
|
|
|
|
void MMU_init(void);
|
|
|
|
/* XXX should be in current.h -- paulus */
|
|
extern struct task_struct *current_set[NR_CPUS];
|
|
|
|
/*
|
|
* 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;
|
|
int __map_without_ltlbs;
|
|
|
|
/*
|
|
* This tells the system to allow ioremapping memory marked as reserved.
|
|
*/
|
|
int __allow_ioremap_reserved;
|
|
|
|
/* max amount of low RAM to map in */
|
|
unsigned long __max_low_memory = MAX_LOW_MEM;
|
|
|
|
/*
|
|
* Check for command-line options that affect what MMU_init will do.
|
|
*/
|
|
void MMU_setup(void)
|
|
{
|
|
/* Check for nobats option (used in mapin_ram). */
|
|
if (strstr(cmd_line, "nobats")) {
|
|
__map_without_bats = 1;
|
|
}
|
|
|
|
if (strstr(cmd_line, "noltlbs")) {
|
|
__map_without_ltlbs = 1;
|
|
}
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
__map_without_bats = 1;
|
|
__map_without_ltlbs = 1;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* MMU_init sets up the basic memory mappings for the kernel,
|
|
* including both RAM and possibly some I/O regions,
|
|
* and sets up the page tables and the MMU hardware ready to go.
|
|
*/
|
|
void __init MMU_init(void)
|
|
{
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("MMU:enter", 0x111);
|
|
|
|
/* parse args from command line */
|
|
MMU_setup();
|
|
|
|
/*
|
|
* Reserve gigantic pages for hugetlb. This MUST occur before
|
|
* lowmem_end_addr is initialized below.
|
|
*/
|
|
reserve_hugetlb_gpages();
|
|
|
|
if (memblock.memory.cnt > 1) {
|
|
#ifndef CONFIG_WII
|
|
memblock.memory.cnt = 1;
|
|
memblock_analyze();
|
|
printk(KERN_WARNING "Only using first contiguous memory region");
|
|
#else
|
|
wii_memory_fixups();
|
|
#endif
|
|
}
|
|
|
|
total_lowmem = total_memory = memblock_end_of_DRAM() - memstart_addr;
|
|
lowmem_end_addr = memstart_addr + total_lowmem;
|
|
|
|
#ifdef CONFIG_FSL_BOOKE
|
|
/* Freescale Book-E parts expect lowmem to be mapped by fixed TLB
|
|
* entries, so we need to adjust lowmem to match the amount we can map
|
|
* in the fixed entries */
|
|
adjust_total_lowmem();
|
|
#endif /* CONFIG_FSL_BOOKE */
|
|
|
|
if (total_lowmem > __max_low_memory) {
|
|
total_lowmem = __max_low_memory;
|
|
lowmem_end_addr = memstart_addr + total_lowmem;
|
|
#ifndef CONFIG_HIGHMEM
|
|
total_memory = total_lowmem;
|
|
memblock_enforce_memory_limit(total_lowmem);
|
|
memblock_analyze();
|
|
#endif /* CONFIG_HIGHMEM */
|
|
}
|
|
|
|
/* Initialize the MMU hardware */
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("MMU:hw init", 0x300);
|
|
MMU_init_hw();
|
|
|
|
/* Map in all of RAM starting at KERNELBASE */
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("MMU:mapin", 0x301);
|
|
mapin_ram();
|
|
|
|
/* Initialize early top-down ioremap allocator */
|
|
ioremap_bot = IOREMAP_TOP;
|
|
|
|
/* Map in I/O resources */
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("MMU:setio", 0x302);
|
|
|
|
if (ppc_md.progress)
|
|
ppc_md.progress("MMU:exit", 0x211);
|
|
|
|
/* From now on, btext is no longer BAT mapped if it was at all */
|
|
#ifdef CONFIG_BOOTX_TEXT
|
|
btext_unmap();
|
|
#endif
|
|
|
|
/* Shortly after that, the entire linear mapping will be available */
|
|
memblock_set_current_limit(lowmem_end_addr);
|
|
}
|
|
|
|
/* This is only called until mem_init is done. */
|
|
void __init *early_get_page(void)
|
|
{
|
|
if (init_bootmem_done)
|
|
return alloc_bootmem_pages(PAGE_SIZE);
|
|
else
|
|
return __va(memblock_alloc(PAGE_SIZE, PAGE_SIZE));
|
|
}
|
|
|
|
#ifdef CONFIG_8xx /* No 8xx specific .c file to put that in ... */
|
|
void setup_initial_memory_limit(phys_addr_t first_memblock_base,
|
|
phys_addr_t first_memblock_size)
|
|
{
|
|
/* We don't currently support the first MEMBLOCK not mapping 0
|
|
* physical on those processors
|
|
*/
|
|
BUG_ON(first_memblock_base != 0);
|
|
|
|
/* 8xx can only access 8MB at the moment */
|
|
memblock_set_current_limit(min_t(u64, first_memblock_size, 0x00800000));
|
|
}
|
|
#endif /* CONFIG_8xx */
|