summaryrefslogtreecommitdiff
path: root/arch/arm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm.c')
-rw-r--r--arch/arm.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/arch/arm.c b/arch/arm.c
new file mode 100644
index 0000000..af7442a
--- /dev/null
+++ b/arch/arm.c
@@ -0,0 +1,169 @@
+/*
+ * arm.c
+ *
+ * Created by: Mika Westerberg <ext-mika.1.westerberg@nokia.com>
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifdef __arm__
+
+#include "../print_info.h"
+#include "../elf_info.h"
+#include "../makedumpfile.h"
+
+#define PMD_TYPE_MASK 3
+#define PMD_TYPE_SECT 2
+#define PMD_TYPE_TABLE 1
+
+#define pgd_index(vaddr) ((vaddr) >> PGDIR_SHIFT)
+#define pte_index(vaddr) ((vaddr >> PAGESHIFT()) & (PTRS_PER_PTE - 1))
+
+#define pgd_offset(pgdir, vaddr) \
+ ((pgdir) + pgd_index(vaddr) * 2 * sizeof(unsigned long))
+#define pmd_offset(dir, vaddr) (dir)
+#define pte_offset(pmd, vaddr) \
+ (pmd_page_vaddr(pmd) + pte_index(vaddr) * sizeof(unsigned long))
+
+/*
+ * These only work for kernel directly mapped addresses.
+ */
+#define __va(paddr) ((paddr) - info->phys_base + info->page_offset)
+#define __pa(vaddr) ((vaddr) - info->page_offset + info->phys_base)
+
+static inline unsigned long
+pmd_page_vaddr(unsigned long pmd)
+{
+ unsigned long ptr;
+
+ ptr = pmd & ~(PTRS_PER_PTE * sizeof(void *) - 1);
+ ptr += PTRS_PER_PTE * sizeof(void *);
+
+ return __va(ptr);
+}
+
+int
+get_phys_base_arm(void)
+{
+ unsigned long phys_base = ULONG_MAX;
+ unsigned long long phys_start;
+ int i;
+
+ /*
+ * We resolve phys_base from PT_LOAD segments. LMA contains physical
+ * address of the segment, and we use the first one.
+ */
+ for (i = 0; get_pt_load(i, &phys_start, NULL, NULL, NULL); i++) {
+ if (phys_start < phys_base)
+ phys_base = phys_start;
+ }
+
+ if (phys_base == ULONG_MAX) {
+ ERRMSG("Can't determine phys_base.\n");
+ return FALSE;
+ }
+
+ info->phys_base = phys_base;
+ DEBUG_MSG("phys_base : %lx\n", phys_base);
+
+ return TRUE;
+}
+
+int
+get_machdep_info_arm(void)
+{
+ info->page_offset = SYMBOL(_stext) & 0xffff0000UL;
+ info->max_physmem_bits = _MAX_PHYSMEM_BITS;
+ info->kernel_start = SYMBOL(_stext);
+ info->section_size_bits = _SECTION_SIZE_BITS;
+
+ DEBUG_MSG("page_offset : %lx\n", info->page_offset);
+ DEBUG_MSG("kernel_start : %lx\n", info->kernel_start);
+
+ return TRUE;
+}
+
+/*
+ * vtop_arm() - translate arbitrary virtual address to physical
+ * @vaddr: virtual address to translate
+ *
+ * Function translates @vaddr into physical address using page tables. This
+ * address can be any virtual address. Returns physical address of the
+ * corresponding virtual address or %NOT_PADDR when there is no translation.
+ */
+static unsigned long long
+vtop_arm(unsigned long vaddr)
+{
+ unsigned long long paddr = NOT_PADDR;
+ unsigned long ptr, pgd, pte, pmd;
+
+ if (SYMBOL(swapper_pg_dir) == NOT_FOUND_SYMBOL) {
+ ERRMSG("Can't get the symbol of swapper_pg_dir.\n");
+ return NOT_PADDR;
+ }
+
+ ptr = pgd_offset(SYMBOL(swapper_pg_dir), vaddr);
+ if (!readmem(VADDR, ptr, &pgd, sizeof(pmd))) {
+ ERRMSG("Can't read pgd\n");
+ return NOT_PADDR;
+ }
+
+ if (info->vaddr_for_vtop == vaddr)
+ MSG(" PGD : %08lx => %08lx\n", ptr, pgd);
+
+ pmd = pmd_offset(pgd, vaddr);
+
+ switch (pmd & PMD_TYPE_MASK) {
+ case PMD_TYPE_TABLE: {
+ /* 4k small page */
+ ptr = pte_offset(pmd, vaddr);
+ if (!readmem(VADDR, ptr, &pte, sizeof(pte))) {
+ ERRMSG("Can't read pte\n");
+ return NOT_PADDR;
+ }
+
+ if (info->vaddr_for_vtop == vaddr)
+ MSG(" PTE : %08lx => %08lx\n", ptr, pte);
+
+ if (!(pte & _PAGE_PRESENT)) {
+ ERRMSG("Can't get a valid pte.\n");
+ return NOT_PADDR;
+ }
+
+ paddr = PAGEBASE(pte) + (vaddr & (PAGESIZE() - 1));
+ break;
+ }
+
+ case PMD_TYPE_SECT:
+ /* 1MB section */
+ pte = pmd & PMD_MASK;
+ paddr = pte + (vaddr & (PMD_SIZE - 1));
+ break;
+ }
+
+ return paddr;
+}
+
+unsigned long long
+vaddr_to_paddr_arm(unsigned long vaddr)
+{
+ /*
+ * Only use translation tables when user has explicitly requested us to
+ * perform translation for a given address. Otherwise we assume that the
+ * translation is done within the kernel direct mapped region.
+ */
+ if (info->vaddr_for_vtop == vaddr)
+ return vtop_arm(vaddr);
+
+ return __pa(vaddr);
+}
+
+#endif /* __arm__ */