[PATH v3 0/3] arm64: fix kernel memory map handling for kaslr-enabled kernel
by AKASHI Takahiro
Not a big jump from v2, but ...
Chnages in v3:
* Refined KASLR handling
hopefully the tool works even on a live system if CONFIG_RANDOMIZE_RAM is
not configured
* Fixed a renaming of a member of struct page
* Removed a commit message regarding an issue of backtracing a panic'ed task
because this is not a bug in this tool, but my kdump patch's.
* Reported "kmem <vmalloc addr>" issue in a commit message
changes in v2:
* Fixed build warnings
* Moved ARM64_NEW_VMEMMAP to machdep->flags
* Show additional kaslr-related parameters in arm64_dump_machdep_table()
* Handle a VMCOREINFO, "NUMBER(kimage_voffset)"
AKASHI Takahiro (3):
arm64: fix kernel memory map handling for kaslr-enabled kernel
fix a renaming of a member of struct page, _count to _refcount
arm64: show a warning for 48-bit kernel with 4KB page
arm64.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
defs.h | 24 +++++--
main.c | 7 +-
memory.c | 6 +-
symbols.c | 12 ++--
5 files changed, 204 insertions(+), 64 deletions(-)
--
2.8.1
8 years, 3 months
[PATCH v2] arm64: fix kernel memory map handling for kaslr-enabled kernel
by AKASHI Takahiro
Yet some issues, but ...
changes in v2:
* Fixed build warnings
* Moved ARM64_NEW_VMEMMAP to machdep->flags
* Show additional kaslr-related parameters in arm64_dump_machdep_table()
* Handle a VMCOREINFO, "NUMBER(kimage_voffset)"
===8<===
>From 080a54ec232ac48ef2d8123cbedcf0f1fe27e391 Mon Sep 17 00:00:00 2001
From: AKASHI Takahiro <takahiro.akashi(a)linaro.org>
Date: Mon, 16 May 2016 17:31:55 +0900
Subject: [PATCH v2] arm64: fix kernel memory map handling for kaslr-enabled
kernel
In kernel v4.6, Kernel ASLR (KASLR) is supported on arm64, and the start
address of the kernel image can be randomized if CONFIG_RANDOMIZE_BASE is
enabled.
Even worse, the kernel image is no more mapped in the linear mapping, but
in vmalloc area (i.e. below PAGE_OFFSET).
Now, according to the kernel's memory.h, converting a virtual address to
a physical address should be done like below:
phys_addr_t __x = (phys_addr_t)(x); \
__x & BIT(VA_BITS - 1) ? (__x & ~PAGE_OFFSET) + PHYS_OFFSET : \
(__x - kimage_voffset); })
Please note that PHYS_OFFSET is no more equal to the start address of
the first usable memory block in SYSTEM RAM due to the fact mentioned
above.
This patch addresses this change and allows the crash utility to access
memory contents with correct addresses.
*However*, it is still rough-edged and we need more refinements.
1-1) On a live system, crash with this patch won't work because
we currently have no way to know kimage_voffset.
1-2) For a core dump file, we can do simply:
$ crash <vmlinux> <vmcore>
as long as the file has "NUMBER(kimage_voffset)"
(RELOC_AUTO|KASLR is automatically set.)
I'm planning to add this enhancement in my next version of kexec/kdump
patch, i.e. v17.
2) My current change breaks the backward compatibility.
Crash won't work with v4.5 or early kernel partly because I changed
the calculation of VA_BITS. (I think that the existing code is wrong.)
So far I don't have enough time to look into this issue as I'm focusing
on KASLR support.
3) Backtracing a 'panic'ed task fails:
crash> bt
PID: 999 TASK: ffffffe960081800 CPU: 0 COMMAND: "sh"
bt: WARNING: corrupt prstatus? pstate=0x80000000, but no user frame found
bt: WARNING: cannot determine starting stack frame for task ffffffe960081800
frame->pc indicates that it is a kernel address, so obviously something
is wrong. I'm diggin into it.
Signed-off-by: AKASHI Takahiro <takahiro.akashi(a)linaro.org>
---
arm64.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
defs.h | 24 +++++--
main.c | 7 +-
symbols.c | 10 +--
4 files changed, 196 insertions(+), 62 deletions(-)
diff --git a/arm64.c b/arm64.c
index 34c8c59..6b97093 100644
--- a/arm64.c
+++ b/arm64.c
@@ -72,6 +72,21 @@ static int arm64_get_crash_notes(void);
static void arm64_calc_VA_BITS(void);
static int arm64_is_uvaddr(ulong, struct task_context *);
+ulong
+arm64_VTOP(ulong addr)
+{
+ if (!(machdep->flags & NEW_VMEMMAP) ||
+ (addr >= machdep->machspec->page_offset)) {
+ return machdep->machspec->phys_offset
+ + (addr - machdep->machspec->page_offset);
+ } else {
+ if (machdep->machspec->kimage_voffset)
+ return addr - machdep->machspec->kimage_voffset;
+ else /* no randomness */
+ return machdep->machspec->phys_offset
+ + (addr - machdep->machspec->vmalloc_start_addr);
+ }
+}
/*
* Do all necessary machine-specific setup here. This is called several times
@@ -81,6 +96,7 @@ void
arm64_init(int when)
{
ulong value;
+ char *string;
struct machine_specific *ms;
#if defined(__x86_64__)
@@ -102,9 +118,34 @@ arm64_init(int when)
if (machdep->cmdline_args[0])
arm64_parse_cmdline_args();
machdep->flags |= MACHDEP_BT_TEXT;
+
+ ms = machdep->machspec;
+ if (!ms->kimage_voffset &&
+ (string = pc->read_vmcoreinfo("NUMBER(kimage_voffset)"))) {
+ ms->kimage_voffset = htol(string, QUIET, NULL);
+ free(string);
+ }
+
+ if (ms->kimage_voffset) {
+ machdep->flags |= NEW_VMEMMAP;
+
+ /* if not specified in command line */
+ if (!kt->relocate && !(kt->flags2 & (RELOC_AUTO|KASLR)))
+ kt->flags2 |= (RELOC_AUTO|KASLR);
+ }
+
break;
case PRE_GDB:
+ /* This check is somewhat redundant */
+ if (kernel_symbol_exists("kimage_voffset")) {
+ machdep->flags |= NEW_VMEMMAP;
+
+ if (!machdep->machspec->kimage_voffset)
+ error(WARNING,
+ "kimage_voffset not identified for kaslr kernel\n");
+ }
+
if (!machdep->pagesize) {
/*
* Kerneldoc Documentation/arm64/booting.txt describes
@@ -160,16 +201,35 @@ arm64_init(int when)
machdep->pagemask = ~((ulonglong)machdep->pageoffset);
arm64_calc_VA_BITS();
- machdep->machspec->page_offset = ARM64_PAGE_OFFSET;
+ ms = machdep->machspec;
+ ms->page_offset = ARM64_PAGE_OFFSET;
+ /* FIXME: idmap for NEW_VMEMMAP */
machdep->identity_map_base = ARM64_PAGE_OFFSET;
- machdep->machspec->userspace_top = ARM64_USERSPACE_TOP;
- machdep->machspec->modules_vaddr = ARM64_MODULES_VADDR;
- machdep->machspec->modules_end = ARM64_MODULES_END;
- machdep->machspec->vmalloc_start_addr = ARM64_VMALLOC_START;
- machdep->machspec->vmalloc_end = ARM64_VMALLOC_END;
- machdep->kvbase = ARM64_VMALLOC_START;
- machdep->machspec->vmemmap_vaddr = ARM64_VMEMMAP_VADDR;
- machdep->machspec->vmemmap_end = ARM64_VMEMMAP_END;
+ machdep->kvbase = ARM64_VA_START;
+ ms->userspace_top = ARM64_USERSPACE_TOP;
+ if (machdep->flags & NEW_VMEMMAP) {
+ struct syment *sp;
+
+ sp = kernel_symbol_search("_text");
+ ms->kimage_text = (sp ? sp->value : 0);
+ sp = kernel_symbol_search("_end");
+ ms->kimage_end = (sp ? sp->value : 0);
+
+ ms->modules_vaddr = ARM64_VA_START;
+ if (kernel_symbol_exists("kasan_init"))
+ ms->modules_vaddr += ARM64_KASAN_SHADOW_SIZE;
+ ms->modules_end = ms->modules_vaddr
+ + ARM64_MODULES_VSIZE -1;
+
+ ms->vmalloc_start_addr = ms->modules_end + 1;
+ } else {
+ ms->modules_vaddr = ARM64_PAGE_OFFSET - MEGABYTES(64);
+ ms->modules_end = ARM64_PAGE_OFFSET - 1;
+ ms->vmalloc_start_addr = ARM64_VA_START;
+ }
+ ms->vmalloc_end = ARM64_VMALLOC_END;
+ ms->vmemmap_vaddr = ARM64_VMEMMAP_VADDR;
+ ms->vmemmap_end = ARM64_VMEMMAP_END;
switch (machdep->pagesize)
{
@@ -232,8 +292,6 @@ arm64_init(int when)
machdep->stacksize = ARM64_STACK_SIZE;
machdep->flags |= VMEMMAP;
- arm64_calc_phys_offset();
-
machdep->uvtop = arm64_uvtop;
machdep->kvtop = arm64_kvtop;
machdep->is_kvaddr = generic_is_kvaddr;
@@ -262,6 +320,10 @@ arm64_init(int when)
machdep->dumpfile_init = NULL;
machdep->verify_line_number = NULL;
machdep->init_kernel_pgd = arm64_init_kernel_pgd;
+
+ /* use machdep parameters */
+ arm64_calc_phys_offset();
+
break;
case POST_GDB:
@@ -409,6 +471,8 @@ arm64_dump_machdep_table(ulong arg)
fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : "");
if (machdep->flags & MACHDEP_BT_TEXT)
fprintf(fp, "%sMACHDEP_BT_TEXT", others++ ? "|" : "");
+ if (machdep->flags & NEW_VMEMMAP)
+ fprintf(fp, "%sNEW_VMEMMAP", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " kvbase: %lx\n", machdep->kvbase);
@@ -494,6 +558,8 @@ arm64_dump_machdep_table(ulong arg)
ms = machdep->machspec;
fprintf(fp, " machspec: %lx\n", (ulong)ms);
+ fprintf(fp, " memory map layout: %s\n",
+ (machdep->flags & NEW_VMEMMAP) ? "new" : "old");
fprintf(fp, " VA_BITS: %ld\n", ms->VA_BITS);
fprintf(fp, " userspace_top: %016lx\n", ms->userspace_top);
fprintf(fp, " page_offset: %016lx\n", ms->page_offset);
@@ -503,6 +569,11 @@ arm64_dump_machdep_table(ulong arg)
fprintf(fp, " modules_end: %016lx\n", ms->modules_end);
fprintf(fp, " vmemmap_vaddr: %016lx\n", ms->vmemmap_vaddr);
fprintf(fp, " vmemmap_end: %016lx\n", ms->vmemmap_end);
+ if (machdep->flags & NEW_VMEMMAP) {
+ fprintf(fp, " kimage_text: %016lx\n", ms->kimage_text);
+ fprintf(fp, " kimage_end: %016lx\n", ms->kimage_end);
+ fprintf(fp, " kimage_voffset: %016lx\n", ms->kimage_voffset);
+ }
fprintf(fp, " phys_offset: %lx\n", ms->phys_offset);
fprintf(fp, "__exception_text_start: %lx\n", ms->__exception_text_start);
fprintf(fp, " __exception_text_end: %lx\n", ms->__exception_text_end);
@@ -543,6 +614,42 @@ arm64_dump_machdep_table(ulong arg)
}
}
+static int
+arm64_parse_machdep_arg_l(char *argstring, char *param, ulong *value)
+{
+ int len;
+ int megabytes = FALSE;
+ char *p;
+
+ len = strlen(param);
+ if (!STRNEQ(argstring, param) || (argstring[len] != '='))
+ return FALSE;
+
+ if ((LASTCHAR(argstring) == 'm') ||
+ (LASTCHAR(argstring) == 'M')) {
+ LASTCHAR(argstring) = NULLCHAR;
+ megabytes = TRUE;
+ }
+
+ p = argstring + len + 1;
+ if (strlen(p)) {
+ int flags = RETURN_ON_ERROR | QUIET;
+ int err = 0;
+
+ if (megabytes) {
+ *value = dtol(p, flags, &err);
+ if (!err)
+ *value = MEGABYTES(*value);
+ } else {
+ *value = htol(p, flags, &err);
+ }
+
+ if (!err)
+ return TRUE;
+ }
+
+ return FALSE;
+}
/*
* Parse machine dependent command line arguments.
@@ -554,11 +661,10 @@ arm64_dump_machdep_table(ulong arg)
static void
arm64_parse_cmdline_args(void)
{
- int index, i, c, err;
+ int index, i, c;
char *arglist[MAXARGS];
char buf[BUFSIZE];
char *p;
- ulong value = 0;
for (index = 0; index < MAX_MACHDEP_ARGS; index++) {
if (!machdep->cmdline_args[index])
@@ -580,39 +686,23 @@ arm64_parse_cmdline_args(void)
c = parse_line(buf, arglist);
for (i = 0; i < c; i++) {
- err = 0;
-
- if (STRNEQ(arglist[i], "phys_offset=")) {
- int megabytes = FALSE;
- int flags = RETURN_ON_ERROR | QUIET;
-
- if ((LASTCHAR(arglist[i]) == 'm') ||
- (LASTCHAR(arglist[i]) == 'M')) {
- LASTCHAR(arglist[i]) = NULLCHAR;
- megabytes = TRUE;
- }
-
- p = arglist[i] + strlen("phys_offset=");
- if (strlen(p)) {
- if (megabytes)
- value = dtol(p, flags, &err);
- else
- value = htol(p, flags, &err);
- }
-
- if (!err) {
- if (megabytes)
- value = MEGABYTES(value);
-
- machdep->machspec->phys_offset = value;
-
- error(NOTE,
- "setting phys_offset to: 0x%lx\n\n",
- machdep->machspec->phys_offset);
+ if (arm64_parse_machdep_arg_l(arglist[i],
+ "phys_offset",
+ &machdep->machspec->phys_offset)) {
+ error(NOTE,
+ "setting phys_offset to: 0x%lx\n\n",
+ machdep->machspec->phys_offset);
+
+ machdep->flags |= PHYS_OFFSET;
+ continue;
+ } else if (arm64_parse_machdep_arg_l(arglist[i],
+ "kimage_voffset",
+ &machdep->machspec->kimage_voffset)) {
+ error(NOTE,
+ "setting kimage_voffset to: 0x%lx\n\n",
+ machdep->machspec->kimage_voffset);
- machdep->flags |= PHYS_OFFSET;
- continue;
- }
+ continue;
}
error(WARNING, "ignoring --machdep option: %s\n",
@@ -627,10 +717,20 @@ arm64_calc_phys_offset(void)
{
struct machine_specific *ms = machdep->machspec;
ulong phys_offset;
+ struct syment *sp;
+ ulong value;
if (machdep->flags & PHYS_OFFSET) /* --machdep override */
return;
+ sp = kernel_symbol_search("memstart_addr");
+ if (sp && readmem(sp->value, KVADDR, (char *)&value, sizeof(value),
+ "memstart_addr", QUIET|RETURN_ON_ERROR)) {
+ ms->phys_offset = value;
+ machdep->flags |= PHYS_OFFSET;
+ return;
+ }
+
/*
* Next determine suitable value for phys_offset. User can override this
* by passing valid '--machdep phys_offset=<addr>' option.
@@ -2377,6 +2477,11 @@ arm64_IS_VMALLOC_ADDR(ulong vaddr)
{
struct machine_specific *ms = machdep->machspec;
+ if ((machdep->flags & NEW_VMEMMAP) &&
+ (vaddr >= machdep->machspec->kimage_text) &&
+ (vaddr <= machdep->machspec->kimage_end))
+ return FALSE;
+
return ((vaddr >= ms->vmalloc_start_addr && vaddr <= ms->vmalloc_end) ||
((machdep->flags & VMEMMAP) &&
(vaddr >= ms->vmemmap_vaddr && vaddr <= ms->vmemmap_end)) ||
@@ -2407,7 +2512,11 @@ arm64_calc_VA_BITS(void)
for (bitval = highest_bit_long(value); bitval; bitval--) {
if ((value & (1UL << bitval)) == 0) {
+#if 1
+ machdep->machspec->VA_BITS = bitval + 1;
+#else /* FIXME: bug? */
machdep->machspec->VA_BITS = bitval + 2;
+#endif
break;
}
}
@@ -2459,10 +2568,22 @@ arm64_calc_virtual_memory_ranges(void)
break;
}
- vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE);
+ if (machdep->flags & NEW_VMEMMAP)
+#define STRUCT_PAGE_MAX_SHIFT 6
+ vmemmap_size = 1UL << (ms->VA_BITS - machdep->pageshift - 1
+ + STRUCT_PAGE_MAX_SHIFT);
+ else
+ vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE);
+
vmalloc_end = (ms->page_offset - PUD_SIZE - vmemmap_size - SZ_64K);
- vmemmap_start = vmalloc_end + SZ_64K;
- vmemmap_end = vmemmap_start + vmemmap_size;
+
+ if (machdep->flags & NEW_VMEMMAP) {
+ vmemmap_start = ms->page_offset - vmemmap_size;
+ vmemmap_end = ms->page_offset;
+ } else {
+ vmemmap_start = vmalloc_end + SZ_64K;
+ vmemmap_end = vmemmap_start + vmemmap_size;
+ }
ms->vmalloc_end = vmalloc_end - 1;
ms->vmemmap_vaddr = vmemmap_start;
diff --git a/defs.h b/defs.h
index a09fa9a..2bbb55b 100644
--- a/defs.h
+++ b/defs.h
@@ -2844,8 +2844,8 @@ typedef u64 pte_t;
#define PTOV(X) \
((unsigned long)(X)-(machdep->machspec->phys_offset)+(machdep->machspec->page_offset))
-#define VTOP(X) \
- ((unsigned long)(X)-(machdep->machspec->page_offset)+(machdep->machspec->phys_offset))
+
+#define VTOP(X) arm64_VTOP((ulong)(X))
#define USERSPACE_TOP (machdep->machspec->userspace_top)
#define PAGE_OFFSET (machdep->machspec->page_offset)
@@ -2938,18 +2938,23 @@ typedef signed int s32;
#define VM_L3_4K (0x10)
#define KDUMP_ENABLED (0x20)
#define IRQ_STACKS (0x40)
+#define NEW_VMEMMAP (0x80)
/*
* sources: Documentation/arm64/memory.txt
* arch/arm64/include/asm/memory.h
* arch/arm64/include/asm/pgtable.h
*/
-
-#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) << (machdep->machspec->VA_BITS - 1))
+#define ARM64_VA_START ((0xffffffffffffffffUL) \
+ << machdep->machspec->VA_BITS)
+#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) \
+ << (machdep->machspec->VA_BITS - 1))
#define ARM64_USERSPACE_TOP ((1UL) << machdep->machspec->VA_BITS)
-#define ARM64_MODULES_VADDR (ARM64_PAGE_OFFSET - MEGABYTES(64))
-#define ARM64_MODULES_END (ARM64_PAGE_OFFSET - 1)
-#define ARM64_VMALLOC_START ((0xffffffffffffffffUL) << machdep->machspec->VA_BITS)
+
+/* only used for v4.6 or later */
+#define ARM64_MODULES_VSIZE MEGABYTES(128)
+#define ARM64_KASAN_SHADOW_SIZE (1UL << (machdep->machspec->VA_BITS - 3))
+
/*
* The following 3 definitions are the original values, but are obsolete
* for 3.17 and later kernels because they are now build-time calculations.
@@ -3028,6 +3033,10 @@ struct machine_specific {
ulong kernel_flags;
ulong irq_stack_size;
ulong *irq_stacks;
+ /* only needed for kaslr-enabled kernel */
+ ulong kimage_voffset;
+ ulong kimage_text;
+ ulong kimage_end;
};
struct arm64_stackframe {
@@ -5385,6 +5394,7 @@ void unwind_backtrace(struct bt_info *);
#ifdef ARM64
void arm64_init(int);
void arm64_dump_machdep_table(ulong);
+ulong arm64_VTOP(ulong);
int arm64_IS_VMALLOC_ADDR(ulong);
ulong arm64_swp_type(ulong);
ulong arm64_swp_offset(ulong);
diff --git a/main.c b/main.c
index 821bb4e..3f24908 100644
--- a/main.c
+++ b/main.c
@@ -227,9 +227,10 @@ main(int argc, char **argv)
optarg);
}
} else if (STREQ(long_options[option_index].name, "kaslr")) {
- if (!machine_type("X86_64"))
- error(INFO, "--kaslr only valid "
- "with X86_64 machine type.\n");
+ if (!machine_type("X86_64") &&
+ !machine_type("ARM64"))
+ error(INFO, "--kaslr not valid "
+ "with this machine type.\n");
else if (STREQ(optarg, "auto"))
kt->flags2 |= (RELOC_AUTO|KASLR);
else {
diff --git a/symbols.c b/symbols.c
index a8d3563..df27793 100644
--- a/symbols.c
+++ b/symbols.c
@@ -593,7 +593,8 @@ kaslr_init(void)
{
char *string;
- if (!machine_type("X86_64") || (kt->flags & RELOC_SET))
+ if ((!machine_type("X86_64") && !machine_type("ARM64")) ||
+ (kt->flags & RELOC_SET))
return;
/*
@@ -712,7 +713,7 @@ store_symbols(bfd *abfd, int dynamic, void *minisyms, long symcount,
if (machine_type("X86")) {
if (!(kt->flags & RELOC_SET))
kt->flags |= RELOC_FORCE;
- } else if (machine_type("X86_64")) {
+ } else if (machine_type("X86_64") || machine_type("ARM64")) {
if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET))
derive_kaslr_offset(abfd, dynamic, from,
fromend, size, store);
@@ -783,7 +784,8 @@ store_sysmap_symbols(void)
error(FATAL, "symbol table namespace malloc: %s\n",
strerror(errno));
- if (!machine_type("X86") && !machine_type("X86_64"))
+ if (!machine_type("X86") && !machine_type("X86_64") &&
+ !machine_type("ARM64"))
kt->flags &= ~RELOC_SET;
first = 0;
@@ -4681,7 +4683,7 @@ value_search(ulong value, ulong *offset)
if ((sp = machdep->value_to_symbol(value, offset)))
return sp;
- if (IS_VMALLOC_ADDR(value))
+ if (IS_VMALLOC_ADDR(value))
goto check_modules;
if ((sp = symval_hash_search(value)) == NULL)
--
2.8.1
8 years, 3 months
[PATCH v1] arm64: fix kernel memory map handling for kaslr-enabled
by AKASHI Takahiro
Hi,
This patch is still rough-edged, but please review it and
any comments are very welcome.
I will try to fix the known issues before I submit a new
version of kexec/kdump patch for v4.7 merge window.
Thanks,
-Takahiro AKASHI
===8<===
>From fdc7c881d98ef00ed1ff38a058b4913a1d5bcda6 Mon Sep 17 00:00:00 2001
From: AKASHI Takahiro <takahiro.akashi(a)linaro.org>
Date: Mon, 16 May 2016 17:31:55 +0900
Subject: [PATCH v1] arm64: fix kernel memory map handling for kaslr-enabled
kernel
In kernel v4.6, Kernel ASLR (KASLR) is supported on arm64, and the start
address of the kernel image can be randomized if CONFIG_RANDOMIZE_BASE is
enabled.
Even worse, the kernel image is no more mapped in the linear mapping, but
in vmalloc area (i.e. below PAGE_OFFSET).
Now, according to the kernel's memory.h, converting a virtual address to
a physical address should be done like below:
phys_addr_t __x = (phys_addr_t)(x); \
__x & BIT(VA_BITS - 1) ? (__x & ~PAGE_OFFSET) + PHYS_OFFSET : \
(__x - kimage_voffset); })
Please note that PHYS_OFFSET is no more equal to the start address of
the first usable memory block in SYSTEM RAM due to the fact mentioned
above.
This patch addresses this change and allows the crash utility to access
memory contents with correct addresses.
*However*, it is still rough-edged and we need more refinements.
1) Currently we have to specify the following command line:
$ crash --kaslr auto
--machdep phys_offset=XXX
--machdep kimage_voffset=YYY
<vmlinux> <vmcore>
To remove these extra options, I'm planning to enhance my kdump patch
so that /proc/vmcore holds the necessary information for analyzing the
contents of kernel memory.
2) My current change breaks the backward compatibility.
Crash won't work with v4.5 or early kernel partly because I changed
the calculation of VA_BITS. (I think that the existing code is wrong.)
So far I don't have enough time to look into this issue as I'm focusing
on KASLR support.
3) Backtracing a 'panic'ed task fails:
crash> bt
PID: 999 TASK: ffffffe960081800 CPU: 0 COMMAND: "sh"
bt: WARNING: corrupt prstatus? pstate=0x80000000, but no user frame found
bt: WARNING: cannot determine starting stack frame for task ffffffe960081800
frame->pc indicates that it is a kernel address, so obviously something
is wrong. I'm diggin into it.
Signed-off-by: AKASHI Takahiro <takahiro.akashi(a)linaro.org>
---
arm64.c | 165 +++++++++++++++++++++++++++++++++++++++++++++-----------------
defs.h | 22 ++++++---
main.c | 7 +--
symbols.c | 10 ++--
4 files changed, 146 insertions(+), 58 deletions(-)
diff --git a/arm64.c b/arm64.c
index 34c8c59..22ddade 100644
--- a/arm64.c
+++ b/arm64.c
@@ -22,6 +22,8 @@
#include <endian.h>
#define NOT_IMPLEMENTED(X) error((X), "%s: function not implemented\n", __func__)
+/* FIXME: temporarily used in kt->flags2 */
+#define ARM64_NEW_VMEMMAP 0x80000000
static struct machine_specific arm64_machine_specific = { 0 };
static int arm64_verify_symbol(const char *, ulong, char);
@@ -72,6 +74,21 @@ static int arm64_get_crash_notes(void);
static void arm64_calc_VA_BITS(void);
static int arm64_is_uvaddr(ulong, struct task_context *);
+ulong
+arm64_VTOP(ulong addr)
+{
+ if (!(kt->flags2 & ARM64_NEW_VMEMMAP) ||
+ (addr >= machdep->machspec->page_offset)) {
+ return machdep->machspec->phys_offset
+ + (addr - machdep->machspec->page_offset);
+ } else {
+ if (machdep->machspec->kimage_voffset)
+ return addr - machdep->machspec->kimage_voffset;
+ else /* no randomness */
+ return machdep->machspec->phys_offset
+ + (addr - machdep->machspec->vmalloc_start_addr);
+ }
+}
/*
* Do all necessary machine-specific setup here. This is called several times
@@ -105,6 +122,10 @@ arm64_init(int when)
break;
case PRE_GDB:
+ /* FIXME: Is kimage_voffset appropriate? */
+ if (kernel_symbol_exists("kimage_voffset"))
+ kt->flags2 |= ARM64_NEW_VMEMMAP;
+
if (!machdep->pagesize) {
/*
* Kerneldoc Documentation/arm64/booting.txt describes
@@ -160,16 +181,33 @@ arm64_init(int when)
machdep->pagemask = ~((ulonglong)machdep->pageoffset);
arm64_calc_VA_BITS();
- machdep->machspec->page_offset = ARM64_PAGE_OFFSET;
+ ms = machdep->machspec;
+ ms->page_offset = ARM64_PAGE_OFFSET;
machdep->identity_map_base = ARM64_PAGE_OFFSET;
- machdep->machspec->userspace_top = ARM64_USERSPACE_TOP;
- machdep->machspec->modules_vaddr = ARM64_MODULES_VADDR;
- machdep->machspec->modules_end = ARM64_MODULES_END;
- machdep->machspec->vmalloc_start_addr = ARM64_VMALLOC_START;
- machdep->machspec->vmalloc_end = ARM64_VMALLOC_END;
- machdep->kvbase = ARM64_VMALLOC_START;
- machdep->machspec->vmemmap_vaddr = ARM64_VMEMMAP_VADDR;
- machdep->machspec->vmemmap_end = ARM64_VMEMMAP_END;
+ ms->userspace_top = ARM64_USERSPACE_TOP;
+ if (kt->flags2 & ARM64_NEW_VMEMMAP) {
+ struct syment *sp;
+
+ sp = kernel_symbol_search("_text");
+ ms->kimage_text = (sp ? sp->value : 0);
+ sp = kernel_symbol_search("_end");
+ ms->kimage_end = (sp ? sp->value : 0);
+
+ ms->modules_vaddr = ARM64_VA_START;
+ if (kernel_symbol_exists("kasan_init"))
+ ms->modules_vaddr += ARM64_KASAN_SHADOW_SIZE;
+ ms->modules_end = ms->modules_vaddr
+ + ARM64_MODULES_VSIZE -1;
+
+ ms->vmalloc_start_addr = ms->modules_end + 1;
+ } else {
+ ms->modules_vaddr = ARM64_PAGE_OFFSET - MEGABYTES(64);
+ ms->modules_end = ARM64_PAGE_OFFSET - 1;
+ ms->vmalloc_start_addr = ARM64_VA_START;
+ }
+ ms->vmalloc_end = ARM64_VMALLOC_END;
+ ms->vmemmap_vaddr = ARM64_VMEMMAP_VADDR;
+ ms->vmemmap_end = ARM64_VMEMMAP_END;
switch (machdep->pagesize)
{
@@ -543,6 +581,42 @@ arm64_dump_machdep_table(ulong arg)
}
}
+static int
+arm64_parse_machdep_arg_l(char *argstring, char *param, ulong *value)
+{
+ int len;
+ int megabytes = FALSE;
+ char *p;
+
+ len = strlen(param);
+ if (!STRNEQ(argstring, param) || (argstring[len] != '='))
+ return FALSE;
+
+ if ((LASTCHAR(argstring) == 'm') ||
+ (LASTCHAR(argstring) == 'M')) {
+ LASTCHAR(argstring) = NULLCHAR;
+ megabytes = TRUE;
+ }
+
+ p = argstring + len + 1;
+ if (strlen(p)) {
+ int flags = RETURN_ON_ERROR | QUIET;
+ int err = 0;
+
+ if (megabytes) {
+ *value = dtol(p, flags, &err);
+ if (!err)
+ *value = MEGABYTES(*value);
+ } else {
+ *value = htol(p, flags, &err);
+ }
+
+ if (!err)
+ return TRUE;
+ }
+
+ return FALSE;
+}
/*
* Parse machine dependent command line arguments.
@@ -580,39 +654,23 @@ arm64_parse_cmdline_args(void)
c = parse_line(buf, arglist);
for (i = 0; i < c; i++) {
- err = 0;
-
- if (STRNEQ(arglist[i], "phys_offset=")) {
- int megabytes = FALSE;
- int flags = RETURN_ON_ERROR | QUIET;
-
- if ((LASTCHAR(arglist[i]) == 'm') ||
- (LASTCHAR(arglist[i]) == 'M')) {
- LASTCHAR(arglist[i]) = NULLCHAR;
- megabytes = TRUE;
- }
-
- p = arglist[i] + strlen("phys_offset=");
- if (strlen(p)) {
- if (megabytes)
- value = dtol(p, flags, &err);
- else
- value = htol(p, flags, &err);
- }
-
- if (!err) {
- if (megabytes)
- value = MEGABYTES(value);
-
- machdep->machspec->phys_offset = value;
-
- error(NOTE,
- "setting phys_offset to: 0x%lx\n\n",
- machdep->machspec->phys_offset);
+ if (arm64_parse_machdep_arg_l(arglist[i],
+ "phys_offset",
+ &machdep->machspec->phys_offset)) {
+ error(NOTE,
+ "setting phys_offset to: 0x%lx\n\n",
+ machdep->machspec->phys_offset);
+
+ machdep->flags |= PHYS_OFFSET;
+ continue;
+ } else if (arm64_parse_machdep_arg_l(arglist[i],
+ "kimage_voffset",
+ &machdep->machspec->kimage_voffset)) {
+ error(NOTE,
+ "setting kimage_voffset to: 0x%lx\n\n",
+ machdep->machspec->kimage_voffset);
- machdep->flags |= PHYS_OFFSET;
- continue;
- }
+ continue;
}
error(WARNING, "ignoring --machdep option: %s\n",
@@ -2377,6 +2435,11 @@ arm64_IS_VMALLOC_ADDR(ulong vaddr)
{
struct machine_specific *ms = machdep->machspec;
+ if ((kt->flags2 & ARM64_NEW_VMEMMAP) &&
+ (vaddr >= machdep->machspec->kimage_text) &&
+ (vaddr <= machdep->machspec->kimage_end))
+ return FALSE;
+
return ((vaddr >= ms->vmalloc_start_addr && vaddr <= ms->vmalloc_end) ||
((machdep->flags & VMEMMAP) &&
(vaddr >= ms->vmemmap_vaddr && vaddr <= ms->vmemmap_end)) ||
@@ -2407,7 +2470,11 @@ arm64_calc_VA_BITS(void)
for (bitval = highest_bit_long(value); bitval; bitval--) {
if ((value & (1UL << bitval)) == 0) {
+#if 1
+ machdep->machspec->VA_BITS = bitval + 1;
+#else /* FIXME: bug? */
machdep->machspec->VA_BITS = bitval + 2;
+#endif
break;
}
}
@@ -2459,10 +2526,20 @@ arm64_calc_virtual_memory_ranges(void)
break;
}
- vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE);
+ if (kt->flags2 & ARM64_NEW_VMEMMAP) {
+#define STRUCT_PAGE_MAX_SHIFT 6
+ vmemmap_size = 1UL << (ms->VA_BITS - machdep->pageshift - 1
+ + STRUCT_PAGE_MAX_SHIFT);
+
+ vmemmap_start = ms->page_offset - vmemmap_size;
+ vmemmap_end = ms->page_offset;
+ } else {
+ vmemmap_size = ALIGN((1UL << (ms->VA_BITS - machdep->pageshift)) * SIZE(page), PUD_SIZE);
+
+ vmemmap_start = vmalloc_end + SZ_64K;
+ vmemmap_end = vmemmap_start + vmemmap_size;
+ }
vmalloc_end = (ms->page_offset - PUD_SIZE - vmemmap_size - SZ_64K);
- vmemmap_start = vmalloc_end + SZ_64K;
- vmemmap_end = vmemmap_start + vmemmap_size;
ms->vmalloc_end = vmalloc_end - 1;
ms->vmemmap_vaddr = vmemmap_start;
diff --git a/defs.h b/defs.h
index a09fa9a..40e02fc 100644
--- a/defs.h
+++ b/defs.h
@@ -2844,8 +2844,8 @@ typedef u64 pte_t;
#define PTOV(X) \
((unsigned long)(X)-(machdep->machspec->phys_offset)+(machdep->machspec->page_offset))
-#define VTOP(X) \
- ((unsigned long)(X)-(machdep->machspec->page_offset)+(machdep->machspec->phys_offset))
+
+#define VTOP(X) arm64_VTOP((ulong)(X))
#define USERSPACE_TOP (machdep->machspec->userspace_top)
#define PAGE_OFFSET (machdep->machspec->page_offset)
@@ -2944,12 +2944,16 @@ typedef signed int s32;
* arch/arm64/include/asm/memory.h
* arch/arm64/include/asm/pgtable.h
*/
-
-#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) << (machdep->machspec->VA_BITS - 1))
+#define ARM64_VA_START ((0xffffffffffffffffUL) \
+ << machdep->machspec->VA_BITS)
+#define ARM64_PAGE_OFFSET ((0xffffffffffffffffUL) \
+ << (machdep->machspec->VA_BITS - 1))
#define ARM64_USERSPACE_TOP ((1UL) << machdep->machspec->VA_BITS)
-#define ARM64_MODULES_VADDR (ARM64_PAGE_OFFSET - MEGABYTES(64))
-#define ARM64_MODULES_END (ARM64_PAGE_OFFSET - 1)
-#define ARM64_VMALLOC_START ((0xffffffffffffffffUL) << machdep->machspec->VA_BITS)
+
+/* only used for v4.6 or later */
+#define ARM64_MODULES_VSIZE MEGABYTES(128)
+#define ARM64_KASAN_SHADOW_SIZE (1UL << (machdep->machspec->VA_BITS - 3))
+
/*
* The following 3 definitions are the original values, but are obsolete
* for 3.17 and later kernels because they are now build-time calculations.
@@ -3028,6 +3032,10 @@ struct machine_specific {
ulong kernel_flags;
ulong irq_stack_size;
ulong *irq_stacks;
+ /* only needed for kaslr-enabled kernel */
+ ulong kimage_voffset;
+ ulong kimage_text;
+ ulong kimage_end;
};
struct arm64_stackframe {
diff --git a/main.c b/main.c
index 821bb4e..3f24908 100644
--- a/main.c
+++ b/main.c
@@ -227,9 +227,10 @@ main(int argc, char **argv)
optarg);
}
} else if (STREQ(long_options[option_index].name, "kaslr")) {
- if (!machine_type("X86_64"))
- error(INFO, "--kaslr only valid "
- "with X86_64 machine type.\n");
+ if (!machine_type("X86_64") &&
+ !machine_type("ARM64"))
+ error(INFO, "--kaslr not valid "
+ "with this machine type.\n");
else if (STREQ(optarg, "auto"))
kt->flags2 |= (RELOC_AUTO|KASLR);
else {
diff --git a/symbols.c b/symbols.c
index a8d3563..df27793 100644
--- a/symbols.c
+++ b/symbols.c
@@ -593,7 +593,8 @@ kaslr_init(void)
{
char *string;
- if (!machine_type("X86_64") || (kt->flags & RELOC_SET))
+ if ((!machine_type("X86_64") && !machine_type("ARM64")) ||
+ (kt->flags & RELOC_SET))
return;
/*
@@ -712,7 +713,7 @@ store_symbols(bfd *abfd, int dynamic, void *minisyms, long symcount,
if (machine_type("X86")) {
if (!(kt->flags & RELOC_SET))
kt->flags |= RELOC_FORCE;
- } else if (machine_type("X86_64")) {
+ } else if (machine_type("X86_64") || machine_type("ARM64")) {
if ((kt->flags2 & RELOC_AUTO) && !(kt->flags & RELOC_SET))
derive_kaslr_offset(abfd, dynamic, from,
fromend, size, store);
@@ -783,7 +784,8 @@ store_sysmap_symbols(void)
error(FATAL, "symbol table namespace malloc: %s\n",
strerror(errno));
- if (!machine_type("X86") && !machine_type("X86_64"))
+ if (!machine_type("X86") && !machine_type("X86_64") &&
+ !machine_type("ARM64"))
kt->flags &= ~RELOC_SET;
first = 0;
@@ -4681,7 +4683,7 @@ value_search(ulong value, ulong *offset)
if ((sp = machdep->value_to_symbol(value, offset)))
return sp;
- if (IS_VMALLOC_ADDR(value))
+ if (IS_VMALLOC_ADDR(value))
goto check_modules;
if ((sp = symval_hash_search(value)) == NULL)
--
2.8.1
8 years, 3 months
[PATCH 1/2] memory.c: fix missing printf flags in INFO message in page_flags_init_from_pageflag_names()
by Andrey Skvortsov
Signed-off-by: Andrey Skvortsov <andrej.skvortzov(a)gmail.com>
---
memory.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/memory.c b/memory.c
index 275fc34..d6271bc 100644
--- a/memory.c
+++ b/memory.c
@@ -6353,7 +6353,7 @@ page_flags_init_from_pageflag_names(void)
}
if (!read_string((ulong)name, namebuf, BUFSIZE-1)) {
- error(INFO, "failed to read pageflag_names entry\n",
+ error(INFO, "failed to read pageflag_names entry (i: %d, name: \"%s\", mask:%d)\n",
i, name, mask);
goto pageflags_fail;
}
--
2.8.1
8 years, 3 months
[PATCH 0/2] qemu: Parse necessary sections in crash added in qemu
by Pankaj Gupta
Qemu migration code added new sections to add features
for live migration of VM. For loading vmcore file captured
with 'virsh dump' we need to parse these sections in crash.
This series contains two patches, which parse these sections:
patch1: parse 'vm_configuration' section
patch2: parse 'vm_footer' section
qemu-load.c | 20 ++++++++++++++++++++
qemu-load.h | 4 +++-
2 files changed, 23 insertions(+), 1 deletion(-)
8 years, 3 months
[PATCH v3 0/9] teach crash to work with "live" ramdump
by Oleg Nesterov
Hi Dave,
Based on your comments, please see the interdiff below.
Changes:
- s/LIVEDUMP/LIVE_RAMDUMP/
- redefine LOCAL_ACTIVE() using LIVE_RAMDUMP
- change pc->dumpfile to be the name of the first ramdump file
- remove the stale label in ramdump_to_elf()
Oleg.
---
diff --git a/defs.h b/defs.h
index d3a03c1..61497a5 100644
--- a/defs.h
+++ b/defs.h
@@ -212,7 +212,7 @@ struct number_option {
#define DEVMEM (0x2000000ULL)
#define REM_LIVE_SYSTEM (0x4000000ULL)
#define NAMELIST_LOCAL (0x8000000ULL)
-#define LIVEDUMP (0x10000000ULL)
+#define LIVE_RAMDUMP (0x10000000ULL)
#define NAMELIST_SAVED (0x20000000ULL)
#define DUMPFILE_SAVED (0x40000000ULL)
#define UNLINK_NAMELIST (0x80000000ULL)
@@ -251,11 +251,11 @@ struct number_option {
#define PROC_KCORE (0x8000000000000000ULL)
#define ACTIVE() (pc->flags & LIVE_SYSTEM)
-#define LOCAL_ACTIVE() ((pc->flags & LIVE_SYSTEM) && (pc->flags2 & MEMSRC_LOCAL))
+#define LOCAL_ACTIVE() ((pc->flags & (LIVE_SYSTEM|LIVE_RAMDUMP)) == LIVE_SYSTEM)
#define DUMPFILE() (!(pc->flags & LIVE_SYSTEM))
#define LIVE() (pc->flags2 & LIVE_DUMP || pc->flags & LIVE_SYSTEM)
-#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN|KVMDUMP|PROC_KCORE|SADUMP|VMWARE_VMSS|LIVEDUMP)
-#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP|KVMDUMP|SADUMP|VMWARE_VMSS|LIVEDUMP)
+#define MEMORY_SOURCES (NETDUMP|KDUMP|MCLXCD|LKCD|DEVMEM|S390D|MEMMOD|DISKDUMP|XENDUMP|CRASHBUILTIN|KVMDUMP|PROC_KCORE|SADUMP|VMWARE_VMSS|LIVE_RAMDUMP)
+#define DUMPFILE_TYPES (DISKDUMP|NETDUMP|KDUMP|MCLXCD|LKCD|S390D|XENDUMP|KVMDUMP|SADUMP|VMWARE_VMSS|LIVE_RAMDUMP)
#define REMOTE() (pc->flags2 & REMOTE_DAEMON)
#define REMOTE_ACTIVE() (pc->flags & REM_LIVE_SYSTEM)
#define REMOTE_DUMPFILE() \
diff --git a/filesys.c b/filesys.c
index 2779b2f..c291298 100644
--- a/filesys.c
+++ b/filesys.c
@@ -124,7 +124,6 @@ fd_init(void)
if (!pc->dumpfile) {
pc->flags |= LIVE_SYSTEM;
- pc->flags2 |= MEMSRC_LOCAL;
get_live_memory_source();
}
@@ -209,8 +208,7 @@ memory_source_init(void)
}
if (pc->dumpfile) {
- if (!(pc->flags & LIVEDUMP) &&
- !file_exists(pc->dumpfile, NULL))
+ if (!file_exists(pc->dumpfile, NULL))
error(FATAL, "%s: %s\n", pc->dumpfile,
strerror(ENOENT));
diff --git a/main.c b/main.c
index 0fbd10a..075a1e8 100644
--- a/main.c
+++ b/main.c
@@ -430,9 +430,7 @@ main(int argc, char **argv)
}
if (ACTIVE()) {
- pc->flags |= LIVEDUMP;
- /* disable get_live_memory_source() logic in fd_init() */
- pc->dumpfile = "livedump";
+ pc->flags |= LIVE_RAMDUMP;
pc->readmem = read_ramdump;
pc->writemem = NULL;
optind++;
diff --git a/memory.c b/memory.c
index 3339aa2..aa8be87 100644
--- a/memory.c
+++ b/memory.c
@@ -16463,7 +16463,7 @@ memory_page_size(void)
case CRASHBUILTIN:
case KVMDUMP:
case PROC_KCORE:
- case LIVEDUMP:
+ case LIVE_RAMDUMP:
psz = (uint)getpagesize();
break;
diff --git a/ramdump.c b/ramdump.c
index 12bfe05..941851c 100644
--- a/ramdump.c
+++ b/ramdump.c
@@ -227,7 +227,7 @@ char *ramdump_to_elf(void)
}
e_file = write_elf(load, e_head, data_offset);
-end:
+
free(e_head);
return e_file;
}
@@ -242,7 +242,7 @@ int is_ramdump(char *p)
size_t len;
char *pattern;
struct stat64 st;
- int is_live = 0;
+ int is_live;
int err = 0;
is_live = PREFIX(p, "live:");
@@ -283,9 +283,11 @@ int is_ramdump(char *p)
pat = NULL;
}
- if (nodes && is_live)
+ if (nodes && is_live) {
pc->flags |= LIVE_SYSTEM;
-
+ pc->dumpfile = ramdump[0].path;
+ pc->live_memsrc = pc->dumpfile;
+ }
return nodes;
}
8 years, 4 months
Fix loading qemu's dump-guest-image
by OGAWA Hirofumi
qemu can make elf vmcore without kdump in kernel. So kernel may not
have "kexec_crash_image" symbol.
Without this patch, kdump_backup_region_init() stops main_loop with
error.
---
netdump.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff -puN netdump.c~check-kexec_crash_image netdump.c
--- crash-64/netdump.c~check-kexec_crash_image 2016-03-29 19:38:08.556253626 +0900
+++ crash-64-hirofumi/netdump.c 2016-03-29 19:38:08.557253630 +0900
@@ -4458,11 +4458,14 @@ kdump_backup_region_init(void)
} else
return;
- if (!readmem(symbol_value("kexec_crash_image"), KVADDR,
- &kexec_crash_image_p, sizeof(ulong),
- "kexec backup region: kexec_crash_image",
- QUIET|RETURN_ON_ERROR))
- goto error;
+ if (symbol_exists("kexec_crash_image")) {
+ if (!readmem(symbol_value("kexec_crash_image"), KVADDR,
+ &kexec_crash_image_p, sizeof(ulong),
+ "kexec backup region: kexec_crash_image",
+ QUIET|RETURN_ON_ERROR))
+ goto error;
+ } else
+ kexec_crash_image_p = 0;
if (!kexec_crash_image_p) {
if (CRASHDEBUG(1))
_
--
OGAWA Hirofumi <hirofumi(a)mail.parknet.co.jp>
8 years, 4 months
PATCH v2 00/10] teach crash to work with "live" ramdump
by Oleg Nesterov
Hi Dave,
please consider V2, I tried to address your comments.
Oleg.
defs.h | 8 +++++---
filesys.c | 10 ++++++----
kernel.c | 9 ++++++---
main.c | 13 ++++++++++++-
memory.c | 1 +
ramdump.c | 32 ++++++++++++++++++--------------
remote.c | 4 ++--
task.c | 13 ++++++-------
tools.c | 2 +-
9 files changed, 57 insertions(+), 35 deletions(-)
8 years, 4 months