|
@@ -22,6 +22,8 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/errno.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/ioport.h>
|
|
|
+#include <linux/kexec.h>
|
|
|
#include <linux/libfdt.h>
|
|
|
#include <linux/mman.h>
|
|
|
#include <linux/nodemask.h>
|
|
@@ -393,10 +395,28 @@ static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
|
|
|
flush_tlb_kernel_range(virt, virt + size);
|
|
|
}
|
|
|
|
|
|
-static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end)
|
|
|
+static void __init __map_memblock(pgd_t *pgd, phys_addr_t start,
|
|
|
+ phys_addr_t end, pgprot_t prot, int flags)
|
|
|
+{
|
|
|
+ __create_pgd_mapping(pgd, start, __phys_to_virt(start), end - start,
|
|
|
+ prot, early_pgtable_alloc, flags);
|
|
|
+}
|
|
|
+
|
|
|
+void __init mark_linear_text_alias_ro(void)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Remove the write permissions from the linear alias of .text/.rodata
|
|
|
+ */
|
|
|
+ update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
|
|
|
+ (unsigned long)__init_begin - (unsigned long)_text,
|
|
|
+ PAGE_KERNEL_RO);
|
|
|
+}
|
|
|
+
|
|
|
+static void __init map_mem(pgd_t *pgd)
|
|
|
{
|
|
|
phys_addr_t kernel_start = __pa_symbol(_text);
|
|
|
phys_addr_t kernel_end = __pa_symbol(__init_begin);
|
|
|
+ struct memblock_region *reg;
|
|
|
int flags = 0;
|
|
|
|
|
|
if (debug_pagealloc_enabled())
|
|
@@ -405,30 +425,28 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
|
|
|
/*
|
|
|
* Take care not to create a writable alias for the
|
|
|
* read-only text and rodata sections of the kernel image.
|
|
|
+ * So temporarily mark them as NOMAP to skip mappings in
|
|
|
+ * the following for-loop
|
|
|
*/
|
|
|
+ memblock_mark_nomap(kernel_start, kernel_end - kernel_start);
|
|
|
+#ifdef CONFIG_KEXEC_CORE
|
|
|
+ if (crashk_res.end)
|
|
|
+ memblock_mark_nomap(crashk_res.start,
|
|
|
+ resource_size(&crashk_res));
|
|
|
+#endif
|
|
|
|
|
|
- /* No overlap with the kernel text/rodata */
|
|
|
- if (end < kernel_start || start >= kernel_end) {
|
|
|
- __create_pgd_mapping(pgd, start, __phys_to_virt(start),
|
|
|
- end - start, PAGE_KERNEL,
|
|
|
- early_pgtable_alloc, flags);
|
|
|
- return;
|
|
|
- }
|
|
|
+ /* map all the memory banks */
|
|
|
+ for_each_memblock(memory, reg) {
|
|
|
+ phys_addr_t start = reg->base;
|
|
|
+ phys_addr_t end = start + reg->size;
|
|
|
|
|
|
- /*
|
|
|
- * This block overlaps the kernel text/rodata mappings.
|
|
|
- * Map the portion(s) which don't overlap.
|
|
|
- */
|
|
|
- if (start < kernel_start)
|
|
|
- __create_pgd_mapping(pgd, start,
|
|
|
- __phys_to_virt(start),
|
|
|
- kernel_start - start, PAGE_KERNEL,
|
|
|
- early_pgtable_alloc, flags);
|
|
|
- if (kernel_end < end)
|
|
|
- __create_pgd_mapping(pgd, kernel_end,
|
|
|
- __phys_to_virt(kernel_end),
|
|
|
- end - kernel_end, PAGE_KERNEL,
|
|
|
- early_pgtable_alloc, flags);
|
|
|
+ if (start >= end)
|
|
|
+ break;
|
|
|
+ if (memblock_is_nomap(reg))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ __map_memblock(pgd, start, end, PAGE_KERNEL, flags);
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
|
* Map the linear alias of the [_text, __init_begin) interval
|
|
@@ -440,37 +458,24 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
|
|
|
* Note that contiguous mappings cannot be remapped in this way,
|
|
|
* so we should avoid them here.
|
|
|
*/
|
|
|
- __create_pgd_mapping(pgd, kernel_start, __phys_to_virt(kernel_start),
|
|
|
- kernel_end - kernel_start, PAGE_KERNEL,
|
|
|
- early_pgtable_alloc, NO_CONT_MAPPINGS);
|
|
|
-}
|
|
|
+ __map_memblock(pgd, kernel_start, kernel_end,
|
|
|
+ PAGE_KERNEL, NO_CONT_MAPPINGS);
|
|
|
+ memblock_clear_nomap(kernel_start, kernel_end - kernel_start);
|
|
|
|
|
|
-void __init mark_linear_text_alias_ro(void)
|
|
|
-{
|
|
|
+#ifdef CONFIG_KEXEC_CORE
|
|
|
/*
|
|
|
- * Remove the write permissions from the linear alias of .text/.rodata
|
|
|
+ * Use page-level mappings here so that we can shrink the region
|
|
|
+ * in page granularity and put back unused memory to buddy system
|
|
|
+ * through /sys/kernel/kexec_crash_size interface.
|
|
|
*/
|
|
|
- update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
|
|
|
- (unsigned long)__init_begin - (unsigned long)_text,
|
|
|
- PAGE_KERNEL_RO);
|
|
|
-}
|
|
|
-
|
|
|
-static void __init map_mem(pgd_t *pgd)
|
|
|
-{
|
|
|
- struct memblock_region *reg;
|
|
|
-
|
|
|
- /* map all the memory banks */
|
|
|
- for_each_memblock(memory, reg) {
|
|
|
- phys_addr_t start = reg->base;
|
|
|
- phys_addr_t end = start + reg->size;
|
|
|
-
|
|
|
- if (start >= end)
|
|
|
- break;
|
|
|
- if (memblock_is_nomap(reg))
|
|
|
- continue;
|
|
|
-
|
|
|
- __map_memblock(pgd, start, end);
|
|
|
+ if (crashk_res.end) {
|
|
|
+ __map_memblock(pgd, crashk_res.start, crashk_res.end + 1,
|
|
|
+ PAGE_KERNEL,
|
|
|
+ NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS);
|
|
|
+ memblock_clear_nomap(crashk_res.start,
|
|
|
+ resource_size(&crashk_res));
|
|
|
}
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
void mark_rodata_ro(void)
|