diff options
author | Huang Ying <ying.huang@intel.com> | 2009-03-09 22:57:04 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-03-10 21:13:25 -0400 |
commit | 5359454701ce51a4626b1ef6eb7b16ec35bd458d (patch) | |
tree | 44f8d60b3cb1d06ec215387eb7e35fdbb64e70c8 | |
parent | fef3a7a17418814733ebde0b40d8e32747677c8f (diff) |
x86, kexec: x86_64: add identity map for pages at image->start
Impact: Fix corner case that cannot yet occur
image->start may be outside of 0 ~ max_pfn, for example when jumping
back to original kernel from kexeced kenrel. This patch add identity
map for pages at image->start.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | arch/x86/kernel/machine_kexec_64.c | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index f8c796fffa0f..7cc5d3d01483 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c | |||
@@ -18,6 +18,41 @@ | |||
18 | #include <asm/tlbflush.h> | 18 | #include <asm/tlbflush.h> |
19 | #include <asm/mmu_context.h> | 19 | #include <asm/mmu_context.h> |
20 | 20 | ||
21 | static int init_one_level2_page(struct kimage *image, pgd_t *pgd, | ||
22 | unsigned long addr) | ||
23 | { | ||
24 | pud_t *pud; | ||
25 | pmd_t *pmd; | ||
26 | struct page *page; | ||
27 | int result = -ENOMEM; | ||
28 | |||
29 | addr &= PMD_MASK; | ||
30 | pgd += pgd_index(addr); | ||
31 | if (!pgd_present(*pgd)) { | ||
32 | page = kimage_alloc_control_pages(image, 0); | ||
33 | if (!page) | ||
34 | goto out; | ||
35 | pud = (pud_t *)page_address(page); | ||
36 | memset(pud, 0, PAGE_SIZE); | ||
37 | set_pgd(pgd, __pgd(__pa(pud) | _KERNPG_TABLE)); | ||
38 | } | ||
39 | pud = pud_offset(pgd, addr); | ||
40 | if (!pud_present(*pud)) { | ||
41 | page = kimage_alloc_control_pages(image, 0); | ||
42 | if (!page) | ||
43 | goto out; | ||
44 | pmd = (pmd_t *)page_address(page); | ||
45 | memset(pmd, 0, PAGE_SIZE); | ||
46 | set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); | ||
47 | } | ||
48 | pmd = pmd_offset(pud, addr); | ||
49 | if (!pmd_present(*pmd)) | ||
50 | set_pmd(pmd, __pmd(addr | __PAGE_KERNEL_LARGE_EXEC)); | ||
51 | result = 0; | ||
52 | out: | ||
53 | return result; | ||
54 | } | ||
55 | |||
21 | static void init_level2_page(pmd_t *level2p, unsigned long addr) | 56 | static void init_level2_page(pmd_t *level2p, unsigned long addr) |
22 | { | 57 | { |
23 | unsigned long end_addr; | 58 | unsigned long end_addr; |
@@ -155,6 +190,13 @@ static int init_pgtable(struct kimage *image, unsigned long start_pgtable) | |||
155 | result = init_level4_page(image, level4p, 0, max_pfn << PAGE_SHIFT); | 190 | result = init_level4_page(image, level4p, 0, max_pfn << PAGE_SHIFT); |
156 | if (result) | 191 | if (result) |
157 | return result; | 192 | return result; |
193 | /* | ||
194 | * image->start may be outside 0 ~ max_pfn, for example when | ||
195 | * jump back to original kernel from kexeced kernel | ||
196 | */ | ||
197 | result = init_one_level2_page(image, level4p, image->start); | ||
198 | if (result) | ||
199 | return result; | ||
158 | return init_transition_pgtable(image, level4p); | 200 | return init_transition_pgtable(image, level4p); |
159 | } | 201 | } |
160 | 202 | ||