diff options
author | Suzuki Poulose <suzuki@in.ibm.com> | 2011-12-14 17:58:37 -0500 |
---|---|---|
committer | Josh Boyer <jwboyer@gmail.com> | 2011-12-20 10:21:34 -0500 |
commit | 368ff8f14d6ed8e9fd3b7c2156f2607719bf5a7a (patch) | |
tree | a52996c9083368fc3c5297bc45d5f67189de9ad7 /arch/powerpc | |
parent | 9c5f7d39a86316cd13baf973c90ed27f9f1cc979 (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.h | 85 | ||||
-rw-r--r-- | arch/powerpc/mm/init_32.c | 7 |
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 | ||
98 | extern phys_addr_t memstart_addr; | 98 | extern phys_addr_t memstart_addr; |
99 | extern phys_addr_t kernstart_addr; | 99 | extern phys_addr_t kernstart_addr; |
100 | |||
101 | #ifdef CONFIG_RELOCATABLE_PPC32 | ||
102 | extern 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; | |||
65 | EXPORT_SYMBOL(memstart_addr); | 65 | EXPORT_SYMBOL(memstart_addr); |
66 | phys_addr_t kernstart_addr; | 66 | phys_addr_t kernstart_addr; |
67 | EXPORT_SYMBOL(kernstart_addr); | 67 | EXPORT_SYMBOL(kernstart_addr); |
68 | |||
69 | #ifdef CONFIG_RELOCATABLE_PPC32 | ||
70 | /* Used in __va()/__pa() */ | ||
71 | long long virt_phys_offset; | ||
72 | EXPORT_SYMBOL(virt_phys_offset); | ||
73 | #endif | ||
74 | |||
68 | phys_addr_t lowmem_end_addr; | 75 | phys_addr_t lowmem_end_addr; |
69 | 76 | ||
70 | int boot_mapsize; | 77 | int boot_mapsize; |