aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorSuzuki Poulose <suzuki@in.ibm.com>2011-12-14 17:58:37 -0500
committerJosh Boyer <jwboyer@gmail.com>2011-12-20 10:21:34 -0500
commit368ff8f14d6ed8e9fd3b7c2156f2607719bf5a7a (patch)
treea52996c9083368fc3c5297bc45d5f67189de9ad7 /arch/powerpc
parent9c5f7d39a86316cd13baf973c90ed27f9f1cc979 (diff)
powerpc: Define virtual-physical translations for RELOCATABLE
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>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/include/asm/page.h85
-rw-r--r--arch/powerpc/mm/init_32.c7
2 files changed, 89 insertions, 3 deletions
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h
index f149967ee6b5..f072e974f8a2 100644
--- a/arch/powerpc/include/asm/page.h
+++ b/arch/powerpc/include/asm/page.h
@@ -97,12 +97,26 @@ extern unsigned int HPAGE_SHIFT;
97 97
98extern phys_addr_t memstart_addr; 98extern phys_addr_t memstart_addr;
99extern phys_addr_t kernstart_addr; 99extern phys_addr_t kernstart_addr;
100
101#ifdef CONFIG_RELOCATABLE_PPC32
102extern long long virt_phys_offset;
100#endif 103#endif
104
105#endif /* __ASSEMBLY__ */
101#define PHYSICAL_START kernstart_addr 106#define PHYSICAL_START kernstart_addr
102#else 107
108#else /* !CONFIG_NONSTATIC_KERNEL */
103#define PHYSICAL_START ASM_CONST(CONFIG_PHYSICAL_START) 109#define PHYSICAL_START ASM_CONST(CONFIG_PHYSICAL_START)
104#endif 110#endif
105 111
112/* See Description below for VIRT_PHYS_OFFSET */
113#ifdef CONFIG_RELOCATABLE_PPC32
114#define VIRT_PHYS_OFFSET virt_phys_offset
115#else
116#define VIRT_PHYS_OFFSET (KERNELBASE - PHYSICAL_START)
117#endif
118
119
106#ifdef CONFIG_PPC64 120#ifdef CONFIG_PPC64
107#define MEMORY_START 0UL 121#define MEMORY_START 0UL
108#elif defined(CONFIG_NONSTATIC_KERNEL) 122#elif defined(CONFIG_NONSTATIC_KERNEL)
@@ -125,12 +139,77 @@ extern phys_addr_t kernstart_addr;
125 * determine MEMORY_START until then. However we can determine PHYSICAL_START 139 * determine MEMORY_START until then. However we can determine PHYSICAL_START
126 * from information at hand (program counter, TLB lookup). 140 * from information at hand (program counter, TLB lookup).
127 * 141 *
142 * On BookE with RELOCATABLE (RELOCATABLE_PPC32)
143 *
144 * With RELOCATABLE_PPC32, we support loading the kernel at any physical
145 * address without any restriction on the page alignment.
146 *
147 * We find the runtime address of _stext and relocate ourselves based on
148 * the following calculation:
149 *
150 * virtual_base = ALIGN_DOWN(KERNELBASE,256M) +
151 * MODULO(_stext.run,256M)
152 * and create the following mapping:
153 *
154 * ALIGN_DOWN(_stext.run,256M) => ALIGN_DOWN(KERNELBASE,256M)
155 *
156 * When we process relocations, we cannot depend on the
157 * existing equation for the __va()/__pa() translations:
158 *
159 * __va(x) = (x) - PHYSICAL_START + KERNELBASE
160 *
161 * Where:
162 * PHYSICAL_START = kernstart_addr = Physical address of _stext
163 * KERNELBASE = Compiled virtual address of _stext.
164 *
165 * This formula holds true iff, kernel load address is TLB page aligned.
166 *
167 * In our case, we need to also account for the shift in the kernel Virtual
168 * address.
169 *
170 * E.g.,
171 *
172 * Let the kernel be loaded at 64MB and KERNELBASE be 0xc0000000 (same as PAGE_OFFSET).
173 * In this case, we would be mapping 0 to 0xc0000000, and kernstart_addr = 64M
174 *
175 * Now __va(1MB) = (0x100000) - (0x4000000) + 0xc0000000
176 * = 0xbc100000 , which is wrong.
177 *
178 * Rather, it should be : 0xc0000000 + 0x100000 = 0xc0100000
179 * according to our mapping.
180 *
181 * Hence we use the following formula to get the translations right:
182 *
183 * __va(x) = (x) - [ PHYSICAL_START - Effective KERNELBASE ]
184 *
185 * Where :
186 * PHYSICAL_START = dynamic load address.(kernstart_addr variable)
187 * Effective KERNELBASE = virtual_base =
188 * = ALIGN_DOWN(KERNELBASE,256M) +
189 * MODULO(PHYSICAL_START,256M)
190 *
191 * To make the cost of __va() / __pa() more light weight, we introduce
192 * a new variable virt_phys_offset, which will hold :
193 *
194 * virt_phys_offset = Effective KERNELBASE - PHYSICAL_START
195 * = ALIGN_DOWN(KERNELBASE,256M) -
196 * ALIGN_DOWN(PHYSICALSTART,256M)
197 *
198 * Hence :
199 *
200 * __va(x) = x - PHYSICAL_START + Effective KERNELBASE
201 * = x + virt_phys_offset
202 *
203 * and
204 * __pa(x) = x + PHYSICAL_START - Effective KERNELBASE
205 * = x - virt_phys_offset
206 *
128 * On non-Book-E PPC64 PAGE_OFFSET and MEMORY_START are constants so use 207 * On non-Book-E PPC64 PAGE_OFFSET and MEMORY_START are constants so use
129 * the other definitions for __va & __pa. 208 * the other definitions for __va & __pa.
130 */ 209 */
131#ifdef CONFIG_BOOKE 210#ifdef CONFIG_BOOKE
132#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) - PHYSICAL_START + KERNELBASE)) 211#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET))
133#define __pa(x) ((unsigned long)(x) + PHYSICAL_START - KERNELBASE) 212#define __pa(x) ((unsigned long)(x) - VIRT_PHYS_OFFSET)
134#else 213#else
135#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START)) 214#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START))
136#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START) 215#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START)
diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c
index 161cefde5c15..60a4e4e84e8c 100644
--- a/arch/powerpc/mm/init_32.c
+++ b/arch/powerpc/mm/init_32.c
@@ -65,6 +65,13 @@ phys_addr_t memstart_addr = (phys_addr_t)~0ull;
65EXPORT_SYMBOL(memstart_addr); 65EXPORT_SYMBOL(memstart_addr);
66phys_addr_t kernstart_addr; 66phys_addr_t kernstart_addr;
67EXPORT_SYMBOL(kernstart_addr); 67EXPORT_SYMBOL(kernstart_addr);
68
69#ifdef CONFIG_RELOCATABLE_PPC32
70/* Used in __va()/__pa() */
71long long virt_phys_offset;
72EXPORT_SYMBOL(virt_phys_offset);
73#endif
74
68phys_addr_t lowmem_end_addr; 75phys_addr_t lowmem_end_addr;
69 76
70int boot_mapsize; 77int boot_mapsize;