diff options
author | Yinghai Lu <yinghai@kernel.org> | 2013-01-24 15:20:03 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-01-29 18:26:26 -0500 |
commit | 084d1283986a530828b8898f206adf44d5d3146d (patch) | |
tree | cc05f142d48e05d2e2bcf260ec61d5d351dc77af /arch/x86/kernel/machine_kexec_64.c | |
parent | 577af55d802d9fe114287e750504e09e7c677c9c (diff) |
x86, kexec: Set ident mapping for kernel that is above max_pfn
When first kernel is booted with memmap= or mem= to limit max_pfn.
kexec can load second kernel above that max_pfn.
We need to set ident mapping for whole image in this case instead of just
for first 2M.
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Link: http://lkml.kernel.org/r/1359058816-7615-23-git-send-email-yinghai@kernel.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86/kernel/machine_kexec_64.c')
-rw-r--r-- | arch/x86/kernel/machine_kexec_64.c | 43 |
1 files changed, 37 insertions, 6 deletions
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index b3ea9db39db6..be14ee120c43 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c | |||
@@ -56,6 +56,25 @@ out: | |||
56 | return result; | 56 | return result; |
57 | } | 57 | } |
58 | 58 | ||
59 | static int ident_mapping_init(struct kimage *image, pgd_t *level4p, | ||
60 | unsigned long mstart, unsigned long mend) | ||
61 | { | ||
62 | int result; | ||
63 | |||
64 | mstart = round_down(mstart, PMD_SIZE); | ||
65 | mend = round_up(mend - 1, PMD_SIZE); | ||
66 | |||
67 | while (mstart < mend) { | ||
68 | result = init_one_level2_page(image, level4p, mstart); | ||
69 | if (result) | ||
70 | return result; | ||
71 | |||
72 | mstart += PMD_SIZE; | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
59 | static void init_level2_page(pmd_t *level2p, unsigned long addr) | 78 | static void init_level2_page(pmd_t *level2p, unsigned long addr) |
60 | { | 79 | { |
61 | unsigned long end_addr; | 80 | unsigned long end_addr; |
@@ -184,22 +203,34 @@ err: | |||
184 | return result; | 203 | return result; |
185 | } | 204 | } |
186 | 205 | ||
187 | |||
188 | static int init_pgtable(struct kimage *image, unsigned long start_pgtable) | 206 | static int init_pgtable(struct kimage *image, unsigned long start_pgtable) |
189 | { | 207 | { |
208 | unsigned long mstart, mend; | ||
190 | pgd_t *level4p; | 209 | pgd_t *level4p; |
191 | int result; | 210 | int result; |
211 | int i; | ||
212 | |||
192 | level4p = (pgd_t *)__va(start_pgtable); | 213 | level4p = (pgd_t *)__va(start_pgtable); |
193 | result = init_level4_page(image, level4p, 0, max_pfn << PAGE_SHIFT); | 214 | result = init_level4_page(image, level4p, 0, max_pfn << PAGE_SHIFT); |
194 | if (result) | 215 | if (result) |
195 | return result; | 216 | return result; |
217 | |||
196 | /* | 218 | /* |
197 | * image->start may be outside 0 ~ max_pfn, for example when | 219 | * segments's mem ranges could be outside 0 ~ max_pfn, |
198 | * jump back to original kernel from kexeced kernel | 220 | * for example when jump back to original kernel from kexeced kernel. |
221 | * or first kernel is booted with user mem map, and second kernel | ||
222 | * could be loaded out of that range. | ||
199 | */ | 223 | */ |
200 | result = init_one_level2_page(image, level4p, image->start); | 224 | for (i = 0; i < image->nr_segments; i++) { |
201 | if (result) | 225 | mstart = image->segment[i].mem; |
202 | return result; | 226 | mend = mstart + image->segment[i].memsz; |
227 | |||
228 | result = ident_mapping_init(image, level4p, mstart, mend); | ||
229 | |||
230 | if (result) | ||
231 | return result; | ||
232 | } | ||
233 | |||
203 | return init_transition_pgtable(image, level4p); | 234 | return init_transition_pgtable(image, level4p); |
204 | } | 235 | } |
205 | 236 | ||