On 05/11/18 at 02:00pm, Yanjiang Jin wrote:
> 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); })
>
> We just set PHYS_OFFSET as the start address of the first usable memory
> block in SYSTEM RAM before, but it is defined in kernel as below:
>
> define PHYS_OFFSET ({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })
>
> So we need to calculate PHYS_OFFSET based on some proc nodes.
>
> Without this change, we would get a wrong vmcore.
> Assume that we have a system as below:
>
> Virtual kernel memory layout:
Add Bhupesh to the CC list, he should has thougts for this issue since
he takes care of arm64 issues.
Thanks
Baoquan
>
> memory : 0xffff800000200000 - 0xffff801800000000
>
> The first iomem block:
>
> 00200000-0021ffff : reserved
>
> But in vmcore's elf header, the corresponding memory block as below,
> the last 2M bytes lost due to "iomem" starts from 0x200000.
>
> Type VirtAddr
> LOAD 0xffff80017fe00000
>
> If an application, for example, vmcore-dmesg, wants to access the
> kernel symbol which is located in the last 2M address, it would
> fail with the below error:
>
> "No program header covering vaddr 0xffff8017ffe90000 found kexec bug?"
>
> Signed-off-by: Yanjiang Jin <[email protected]>
> ---
> kexec/arch/arm64/kexec-arm64.c | 100
> +++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 100 insertions(+)
>
> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
> index 62f3758..d9e65fb 100644
> --- a/kexec/arch/arm64/kexec-arm64.c
> +++ b/kexec/arch/arm64/kexec-arm64.c
> @@ -14,6 +14,8 @@
> #include <sys/stat.h>
> #include <linux/elf-em.h>
> #include <elf.h>
> +#include <sys/mman.h>
> +#include <fcntl.h>
>
> #include "kexec.h"
> #include "kexec-arm64.h"
> @@ -33,6 +35,12 @@
> #define PROP_ELFCOREHDR "linux,elfcorehdr"
> #define PROP_USABLE_MEM_RANGE "linux,usable-memory-range"
>
> +#define DEV_MEM "/dev/mem"
> +
> +#define ALIGN_MASK(x, y) (((x) + (y)) & ~(y))
> +#define ALIGN(x, y) ALIGN_MASK(x, (y) - 1)
> +#define BUFSIZE (256)
> +
> /* Global varables the core kexec routines expect. */
>
> unsigned char reuse_initrd;
> @@ -660,6 +668,96 @@ unsigned long phys_to_virt(struct crash_elf_info
> *elf_info,
> return v;
> }
>
> +static uint64_t get_kernel_paddr(void)
> +{
> + uint64_t start;
> +
> + if (parse_iomem_single("Kernel code\n", &start, NULL) == 0) {
> + dbgprintf("kernel load physical addr start = 0x%" PRIu64 "\n",
> + start);
> + return start;
> + }
> +
> + fprintf(stderr, "Cannot determine kernel physical load addr\n");
> + exit(3);
> +}
> +
> +static uint64_t get_kimage_voffset(void)
> +{
> + uint64_t kern_vaddr_start;
> + uint64_t kern_paddr_start;
> +
> + kern_paddr_start = get_kernel_paddr();
> + kern_vaddr_start = get_kernel_sym("_text");
> +
> + return kern_vaddr_start - kern_paddr_start;
> +}
> +
> +static uint64_t kimg_to_phys(uint64_t vaddr)
> +{
> + return vaddr - get_kimage_voffset();
> +}
> +
> +static void *map_addr(int fd, unsigned long size, off_t offset)
> +{
> + unsigned long page_size = getpagesize();
> + unsigned long map_offset = offset & (page_size - 1);
> + size_t len = ALIGN(size + map_offset, page_size);
> + void *result;
> +
> + result = mmap(0, len, PROT_READ, MAP_SHARED, fd, offset - map_offset);
> + if (result == MAP_FAILED) {
> + fprintf(stderr,
> + "Cannot mmap " DEV_MEM " offset: %#llx size: %lu:
> %s\n",
> + (unsigned long long)offset, size, strerror(errno));
> + exit(5);
> + }
> + return result + map_offset;
> +}
> +
> +static void unmap_addr(void *addr, unsigned long size)
> +{
> + unsigned long page_size = getpagesize();
> + unsigned long map_offset = (uintptr_t)addr & (page_size - 1);
> + size_t len = ALIGN(size + map_offset, page_size);
> + int ret;
> +
> + addr -= map_offset;
> +
> + ret = munmap(addr, len);
> + if (ret < 0) {
> + fprintf(stderr, "munmap failed: %s\n",
> + strerror(errno));
> + exit(6);
> + }
> +}
> +
> +static void init_phys_offset(void)
> +{
> + int fd;
> + uint64_t phys_offset;
> + uint64_t memstart_addr_paddr;
> + void *memstart_addr_vaddr;
> +
> + memstart_addr_paddr = kimg_to_phys(get_kernel_sym("memstart_addr"));
> +
> + fd = open(DEV_MEM, O_RDONLY);
> + if (fd < 0) {
> + fprintf(stderr, "Cannot open " DEV_MEM ": %s\n",
> + strerror(errno));
> + exit(3);
> + }
> +
> + memstart_addr_vaddr = map_addr(fd,
> + sizeof(memstart_addr_paddr), memstart_addr_paddr);
> +
> + phys_offset = *(uint64_t *)memstart_addr_vaddr;
> + unmap_addr(memstart_addr_vaddr, sizeof(memstart_addr_paddr));
> + close(fd);
> +
> + set_phys_offset(phys_offset);
> +}
> +
> /**
> * add_segment - Use virt_to_phys when loading elf files.
> */
> @@ -731,6 +829,8 @@ int get_memory_ranges(struct memory_range **range, int
> *ranges,
> unsigned int count;
> int result;
>
> + init_phys_offset();
> +
> result = get_memory_ranges_iomem(array, &count);
>
> *range = result ? NULL : array;
> --
> 1.8.3.1
>
>
>
>
> This email is intended only for the named addressee. It may contain
> information that is confidential/private, legally privileged, or
> copyright-protected, and you should handle it accordingly. If you are not the
> intended recipient, you do not have legal rights to retain, copy, or
> distribute this email or its contents, and should promptly delete the email
> and all electronic copies in your system; do not retain copies in any media.
> If you have received this email in error, please notify the sender promptly.
> Thank you.
>
>
>
> _______________________________________________
> kexec mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/kexec
_______________________________________________
kexec mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/kexec